Inorder Predecessor in a BST asks for the node that comes just before a given target in sorted order. It is a clean exercise in using the BST ordering property instead of doing a full traversal — and the answer splits neatly into two cases.
Problem. Given the root of a binary search tree and a node target in it, return the node whose
value is the largest value strictly less than target.val — the inorder predecessor. If no such node
exists, return None.
Example: in the tree below, the predecessor of 15 is 10, and the predecessor of 20 is 17.
20
/ \
10 30
/ \ \
5 15 35
\
17The slow way first
The brute-force approach: do a full inorder traversal, collect every value into a list (which comes out sorted), find the target, and return the element right before it. That works, but it visits every node — O(n) time and O(n) space — and ignores the whole point of a BST.
The question to ask: can the BST ordering let me walk straight to the answer without touching the whole tree? Yes. The predecessor is never far away — it is always reachable by a single downward walk.
The idea: two cases
Split on whether the target has a left subtree.
- Case 1 — target has a left child. Everything in the left subtree is smaller than the target, and the predecessor is the largest of those: go left once, then follow
rightpointers as far as they go. That rightmost node is the answer. - Case 2 — no left child. The predecessor must be an ancestor — specifically the lowest ancestor under which the target lies in the right subtree. Searching down from the root for the target, that is exactly the last node where we turned right. Track it in
predand update it every time the search goes right.
Both cases are a single straight-line descent, so the work is bounded by the height of the tree.
Walk through it
Step through the animation. We look for the predecessor of 15, which has no left subtree, so we are in Case 2. Starting at the root, 15 < 20 sends us left without changing pred. At 10, 15 > 10, so we turn right — and record pred = 10. We land on 15 itself; it has no left child, so the search unwinds and the last recorded pred, 10, is the answer. The final steps show Case 1 for the predecessor of 20: go left to 10, then right as far as possible, landing on 17.
Pseudocode
if target has a left child:
node = target.left
walk right while node has a right child
return node # max of the left subtree
pred = None
cur = root
while cur is not None:
if target.val > cur.val: # target is to the right
pred = cur # remember this right-turn ancestor
cur = cur.right
else:
cur = cur.left
return predThe Python solution
def inorder_predecessor(root, target):
if target.left:
node = target.left
while node.right:
node = node.right
return node
pred = None
cur = root
while cur:
if target.val > cur.val:
pred = cur
cur = cur.right
else:
cur = cur.left
return pred- Lines 2-6 handle Case 1: if the target has a left subtree, the predecessor is that subtree maximum — one step left, then right until there is no right child.
predis our running answer for Case 2, starting atNone(the target might be the smallest value, with no predecessor).- We search down from the root for the target.
target.val > cur.valmeans the target lives incur's right subtree, so we just turned right. - On every right turn we set
pred = cur. The last such node is the lowest ancestor with the target on its right — exactly the predecessor. - When
target.val <= cur.valwe go left and do not touchpred, because a left turn never produces a smaller-but-closer candidate.
Complexity
| Case | Time | Notes |
|---|---|---|
| Full inorder traversal | O(n) (moderate) | visits every node |
| BST walk (this solution) | O(h) (moderate) | one descent, h = tree height |
O(1) (fast)For a balanced tree the height h is O(log n), so we find the predecessor in logarithmic time using only a couple of pointers — no list, no recursion stack.
When this pattern shows up
Whenever a BST problem asks for a neighbor in sorted order — predecessor, successor, floor, ceiling, or closest value — think "walk down once, track the best candidate." The ordering property means the answer is always on a single root-to-leaf path; you never need a full traversal.
Do not forget Case 2 returns None when the target is the minimum of the tree. Starting pred = None
and only updating it on right turns handles that automatically — if the search never turns right, there
is genuinely no predecessor.
Practice
What is the inorder predecessor of 17 in the example tree, and which case applies?
1. If the target node has a left subtree, where is its inorder predecessor?
2. When the target has no left child, what does pred track?
3. Why is the time complexity O(h) and not O(n)?
4. What does the algorithm return when the target is the smallest value in the BST?