Trees · Binary Search Tree Operations

BST Inorder Successor and Predecessor

Finding the next-smaller and next-larger values around a key (whether or not the key itself is in the tree) in a single root-to-leaf walk, by tracking the last node stepped past in each direction instead of running a full inorder traversal.

  • 6 min read
  • 9 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: successor without the root
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

A key's neighbors in sorted order are its predecessor, the largest value below it, and its successor, the smallest value above it. Listing every value in order finds them in O(n). A BST does better. Both neighbors lie on the path a search for the key takes, so one walk down from the root finds them. That holds even when the key isn't in the tree.

We'll go from simple to more technical:

  1. The pattern: one walk that records a candidate on each side.
  2. A second flavor: the successor of a node with only parent pointers.
  3. Where neighbors are asked for in production (calendars, order books, sorted maps).
  4. What breaks it: absent keys, "the parent is the successor", and sentinel values.

The approach

Walk down as if searching for the key. A node smaller than the key is a predecessor candidate, and you step right. A node larger than the key is a successor candidate, and you step left. Each new candidate replaces the old one on its side.

Here is the walk for 65, which isn't in the tree [50, 30, 70, 20, 40, 60, 80]:

AtCompared with 65Steppredsucc
50smallerright50none
70largerleft5070
60smallerright6070
(null)-stop6070

If the walk finds the key itself, its own subtrees hold anything closer. The predecessor is then the largest value in its left subtree, and the successor is the smallest in its right subtree.

Java

int[] findPreSuc(TreeNode root, int key) {
    int pred = -1, succ = -1;                        // -1 = "none" (values here are non-negative)
    TreeNode cur = root;
    while (cur != null) {
        if (cur.val == key) {
            if (cur.left != null) {                  // predecessor: rightmost node of the left subtree
                TreeNode t = cur.left;
                while (t.right != null) t = t.right;
                pred = t.val;
            }
            if (cur.right != null) {                 // successor: leftmost node of the right subtree
                TreeNode t = cur.right;
                while (t.left != null) t = t.left;
                succ = t.val;
            }
            break;
        } else if (key < cur.val) {
            succ = cur.val;      // larger than key - a successor candidate
            cur = cur.left;
        } else {
            pred = cur.val;      // smaller than key - a predecessor candidate
            cur = cur.right;
        }
    }
    return new int[]{pred, succ};
}

Python

def find_pre_suc(root: TreeNode | None, key: int) -> list[int]:
    pred = succ = -1                     # -1 = "none" (values here are non-negative)
    cur = root
    while cur is not None:
        if cur.val == key:
            if cur.left is not None:     # predecessor: rightmost node of the left subtree
                t = cur.left
                while t.right is not None:
                    t = t.right
                pred = t.val
            if cur.right is not None:    # successor: leftmost node of the right subtree
                t = cur.right
                while t.left is not None:
                    t = t.left
                succ = t.val
            break
        elif key < cur.val:
            succ = cur.val               # larger than key - a successor candidate
            cur = cur.left
        else:
            pred = cur.val               # smaller than key - a predecessor candidate
            cur = cur.right
    return [pred, succ]

Why is replacing a candidate safe? After stepping left from a node A, every node ahead is in A's left subtree, so it's smaller than A. Any later successor candidate sits between the key and A, which is tighter. Right steps work the same way for predecessors. The walk is O(h).

A second flavor: successor without the root

Sometimes you get a node, not a key, and each node has a parent pointer. The same two cases apply:

Java

TreeNode inorderSuccessor(TreeNode node) {
    if (node.right != null) {                        // case 1: leftmost node of the right subtree
        TreeNode cur = node.right;
        while (cur.left != null) cur = cur.left;
        return cur;
    }
    TreeNode cur = node;                             // case 2: climb while we're a right child
    while (cur.parent != null && cur == cur.parent.right) cur = cur.parent;
    return cur.parent;                               // first ancestor reached from its left side (or null)
}

Python

def inorder_successor(node: TreeNode) -> TreeNode | None:
    if node.right is not None:           # case 1: leftmost node of the right subtree
        cur = node.right
        while cur.left is not None:
            cur = cur.left
        return cur
    cur = node                           # case 2: climb while we're a right child
    while cur.parent is not None and cur is cur.parent.right:
        cur = cur.parent
    return cur.parent                    # first ancestor reached from its left side (or None)

Climbing up from a right child reaches a smaller ancestor, whose turn in sorted order is already over. The first ancestor you reach from its left side comes next. If you climb past the root, the node was the largest value.

Where you'll see it

Nearest neighbors in sorted data: the bookings around a time, the price levels around a target in an order book, the next key after a cursor. Java's TreeMap.lowerKey and higherKey are exactly these two.

Which tool for which job?

You needUse
Both neighbors of a key, present or notone walk: a right step records a predecessor, a left step a successor
The next node from a node, no rootparent pointers: the right subtree's minimum, or climb until you come up from a left child
Neighbors in JavaTreeMap.lowerKey / higherKey (floorKey / ceilingKey include the key)
Neighbors in Pythonbisect on a sorted list

Common mistakes

MistakeWhat happensDo this instead
Handling only keys in the treean absent key gets no answerthe candidate walk handles it with no extra code
Returning the parent as the successorwrong when the node is a right childclimb until you arrive from a left child
-1 as "none" when -1 is a real valuea real neighbor and "none" look the samereturn null / None
int next = map.higherKey(k) on the largest keyNullPointerException on unboxingkeep it an Integer and check for null
Listing everything in order to find two neighborsO(n) instead of O(h)one walk down

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. In [50, 30, 70, 20, 40, 60, 80], what are the predecessor and successor of 25 (not in the tree)?

Show answer

20 and 30. The walk goes left at 50 and left at 30, so the successor candidate becomes 30. Then it goes right at 20, so the predecessor is 20, and it falls off.

2. Node 40 in the same tree has no right child. What is its successor, and why isn't it its parent?

Show answer
  1. 40 is the right child of 30, so 30 is smaller and comes first. Climbing from 30 to 50 arrives from the left side, so 50 comes next.

3. Why does overwriting succ on every left step never throw away a better answer?

Show answer

After a left step from A, everything still reachable is smaller than A. So a later successor candidate lies between the key and A, which is tighter on that side.

Key takeaways

  • Both neighbors of a key are on its search path - one O(h) walk finds them, key present or not.
  • A right step records a predecessor candidate, a left step a successor candidate.
  • An exact match adds the left subtree's maximum and the right subtree's minimum.
  • Without the root, climb until you arrive from a left child.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Both neighbors of a keyone candidate walk
Simplest correct codeone walk per side, a match counts as too small or too large
Next node, no rootright subtree's min, else climb from a left child
JavalowerKey / higherKey, floorKey / ceilingKey
Pythonbisect_left / bisect_right

Snippets worth knowing by heart

Java

Integer succ = null;
for (TreeNode cur = root; cur != null; ) {
    if (key < cur.val) { succ = cur.val; cur = cur.left; }
    else cur = cur.right;                   // a match counts as too small
}

Python

i, j = bisect.bisect_left(s, k), bisect.bisect_right(s, k)
pred = s[i - 1] if i > 0 else None
succ = s[j] if j < len(s) else None

The traps

  • An absent key still has neighbors: code that only handles keys in the tree gives no answer. The candidate walk handles both.
  • The parent as successor: true only when the node is a left child. Otherwise climb until you arrive from a left child.
  • A -1 sentinel that can be a real value hides a real neighbor. Return null / None.
  • Unboxing higherKey's null: assigning it to an int throws on the largest key. Keep an Integer and check null.

Staff-level points, one line each

  • 4,000 queries (half absent): one walk, two walks and bisect agreed on every one.
  • A full in-order walk by parent pointers averaged 2.00 edges per step; the worst single step was 13.
  • int next = m.higherKey(80) on the largest key threw NullPointerException.
  • lower/higher are strict; floor/ceiling include the key.
  • The invariant: everything still reachable lies between the two candidates.

Say it in an interview

  • "The neighbors are on the search path, so one walk finds both, whether or not the key is there."
  • "Each left step tightens the successor, and each right step tightens the predecessor."
  • "Without a root, I take the right subtree's minimum or climb until I come up from a left child."
  • "Iterating the whole tree this way costs about two edges per step."

Practice

This skill comes with 9 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice BST Inorder Successor and Predecessor free

Inorder Successor and Predecessor in a BST

MediumProCoding problemJava · Python

Given the root of a binary search tree and a key (which may or may not exist in the tree), find its inorder predecessor (the largest value in the tree strictly smaller than the key) and its inorder successor (the smallest value strictly larger).

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Inorder Successor With Parent Pointers

MediumProCoding problemJava · Python

Every node of a binary search tree has a parent pointer (null for the root). You're given a node - not the root - and must return the node that comes right after it in inorder (the node with the smallest value larger than it), or null if it's the largest.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice BST Inorder Successor and Predecessor free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime