Skip to content

Coding patternsBlind 75

Lowest Common Ancestor of a Binary Search Tree

In a BST, walk down from the root while both nodes are on the same side; the first node that splits them is the answer. Solved two ways, a full tree search in O(n) and a BST walk in O(h), with a step-through, code in five languages and the Big O worked out from the code.

You are given the root of a binary search tree (BST) and two of its nodes, first_node and second_node. In a BST, every value in the left subtree of a node is smaller than the node, and every value in the right subtree is larger.

Return their lowest common ancestor: the lowest node in the tree that has both first_node and second_node in its subtree. A node counts as part of its own subtree, so first_node itself can be the answer.

Trees in the examples are written level by level, from left to right. null marks a missing child.

Example 1

Input: root = [8, 4, 12, 2, 6, 10, 14, null, null, 5, 7], first_node = 5, second_node = 7

Output: 6

5 and 7 are the two children of 6. Nodes 4 and 8 hold them too, but 6 is lower.

84265first7second121014

Example 2

Input: root = [8, 4, 12, 2, 6, 10, 14, null, null, 5, 7], first_node = 4, second_node = 7

Output: 4

7 is in the subtree of 4, and 4 counts as its own ancestor. So 4 is the answer.

Example 3

Input: root = [3, 1], first_node = 3, second_node = 1

Output: 3

1 is the left child of 3. The root holds both.

  • The tree has n nodes, where 2 ≤ n ≤ 100,000.
  • -1,000,000,000 ≤ Node.val ≤ 1,000,000,000
  • All values are different.
  • first_node and second_node are different, and both are in the tree.
Can first_node be the ancestor of second_node?
Yes. Then first_node itself is the answer, because a node counts as its own ancestor.
Are first_node and second_node always in the tree?
Yes. So we never have to report "not found".
Can values repeat?
No. All values are different, so comparing values is enough to tell where a node lives.
Is first_node always smaller than second_node?
No. The order is not given, so the checks must work either way.

The tree is a BST, so one comparison tells which side a value lives on. If first_node and second_node are both on one side of a node, the answer is on that side too. That turns a search over the whole tree into a single path from the root down, a tree traversal that visits one node per level.

This problem has two approaches. There is no useful step between them.

Optimal O(h)

Start at the root and compare both values with it.

  • Both are smaller: both live in the left subtree, so the answer is there too. Go left.
  • Both are larger: go right, for the same reason.
  • Anything else: they split here. One is on the left and one on the right, or one of them is this very node. Return it.

The walk never needs to come back up, so a plain loop does it. No recursion, no stack.

Why it never misses the answer: while both values are on one side, every node on the other side holds neither of them, so it cannot be a common ancestor. At the split node, any node lower down sits on one side only, so it holds at most one of the two. The split node is the lowest one that holds both.

84265first7second121014
first_node, second_node
57
  1. node = root
  2. while node:
  3. if first_node.val < node.val and second_node.val < node.val:
  4. node = node.left
  5. elif first_node.val > node.val and second_node.val > node.val:
  6. node = node.right
  7. else:
  8. return node

Start at the root. In a BST, smaller values live on the left and larger ones on the right. Use that to head for first_node = 5 and second_node = 7 together.

1 / 8
Solution.javasolution.pySolution.cssolution.gosolution.ts
class Solution {
// BST walk: go down from the root toward both values at once.
// The first node that splits them is the answer. One node per level.
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode firstNode, TreeNode secondNode) {
TreeNode node = root;
while (node != null) {
// Both are smaller, so both live in the left subtree.
if (firstNode.val < node.val && secondNode.val < node.val) node = node.left;
// Both are larger, so both live in the right subtree.
else if (firstNode.val > node.val && secondNode.val > node.val) node = node.right;
// They split here, or one of them is this node. Any lower
// node would hold only one of them.
else return node;
}
return null;
}
}
Time O(h)Space O(1)Here is how we get these from the code, in four steps.
  1. 1 Find the work that repeats

    The loop body runs once per node on the path down. Each run does two or four comparisons and one move. That is the same small job every time, so each run counts as 1 step.

    1. node = root
    2. while node:
    3. if first_node.val < node.val and second_node.val < node.val:
    4. node = node.left
    5. elif first_node.val > node.val and second_node.val > node.val:
    6. node = node.right
    7. else:
    8. return node
  2. 2 Count the runs on Example 1

    Example 1 visits 8, then 4, then 6, one node on each of the first 3 levels. At 6 the values split, and the loop returns.

    3 runs.

    run123
    node846
    level123
    resultgo leftgo rightsplit: return
  3. 3 Write the count for any n

    Every run moves one level down, and it never goes back up. So the loop runs at most once per level. Let h be the height of the tree, the number of levels on the longest path down:

    at most h runs

    Check it with Example 1: h is 4, and the loop ran 3 times.

  4. 4 Keep only the part that grows fastest

    There is nothing to drop: the count is already h. How big h is depends on the shape. A balanced BST has h about log n, which is only 17 for 100,000 nodes. A BST built from sorted values is a chain, and then h = n. So the time is O(h): O(log n) when balanced, O(n) in the worst case.

    So the time is O(h).

  5. S Space: count the extra memory the same way

    The loop keeps only node. It is one variable, whatever the size of the tree, so the space is O(1).

    node

2,000 random search trees of 2 to 10 values, built by inserting the values in random order, with a random pair of nodes. The brute force and the optimal solution gave the same answer every time.

Largest inputsResult
100,000 nodes in one long line (the most allowed), asking about the two deepest nodes. Optimal.0.01 s, and under 0.01 MB of extra memory: one pointer walks down in a loop, which is the O(1) space.
100,000 nodes, balanced, asking about two nodes near the bottom. Optimal.Under 0.001 s, and under 0.01 MB of extra memory: the walk goes down only about 17 levels.
100,000 nodes in one long line. Brute force.0.03 s, and about 15 MB of extra memory: 100,000 calls wait at once, which is the O(h) space. The optimal loop needs none of that.