LeetCode #235 Easy

Lowest Common Ancestor of BST

LCA of two nodes in a BST — exploit the ordering.

bsttree
Open on LeetCode ↗
02

Intuition

In a BST you don't need to search both sides: if both targets are smaller than the current node, the LCA is left; both bigger, it's right. The first node between them (inclusive) is the split point — and the LCA.

How to spot this pattern

In a BST the LCA is simply the first node whose value falls between the two targets — because that's exactly where their search paths diverge. No recursion into both subtrees, no bookkeeping: one walk, one comparison per level. Compare this with the general binary-tree LCA, which must explore both sides precisely because it has no ordering to exploit.

03

Approach

1

Walk, don't recurse everywhere

From the root: both p,q < node → left. Both > node → right. Otherwise node splits them → answer.

2

Why the split is the LCA

Any deeper node loses one of the targets to the other side — the split point is the last common node on both search paths.

3

Iterative one-liner walk

No stack, no parent pointers, O(h).

04

Solution & live demo

1class Solution:
2 def lowestCommonAncestor(self, root, p, q):
3 lo, hi = sorted((p.val, q.val))
4 node = root
5 while node:
6 if node.val < lo: node = node.right
7 elif node.val > hi: node = node.left
8 else: return node
05

Common pitfalls

Using the general binary-tree algorithm

✗ Wrong
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if left and right: return root
✓ Right
if node.val < lo: node = node.right
elif node.val > hi: node = node.left
else: return node

That works but visits every node, O(n), and ignores the ordering that makes this problem Easy rather than Medium. The BST tells you which way both targets lie, so only one path is ever walked.

Assuming p is smaller than q

✗ Wrong
if node.val < p.val: node = node.right
elif node.val > q.val: node = node.left
✓ Right
lo, hi = sorted((p.val, q.val))

The problem doesn't promise any order between the two nodes. If q is the smaller one, both comparisons can fail simultaneously and the walk returns the wrong node. Normalising to lo/hi removes the assumption.

Excluding the targets themselves

✗ Wrong
if lo < node.val < hi: return node
✓ Right
else: return node   # lo <= node.val <= hi

A node is allowed to be its own descendant's ancestor, so when node.val equals one of the targets that node is the LCA. Strict comparisons walk straight past it.

06

Edge cases

One target is the other's ancestor

The walk stops at that target (it's not strictly less/greater on one side) — correct.

Degenerate chain BST

O(n) walk but same logic.

07

Complexity

Time
O(h)
Space
O(1)
Single descent, no subtree exploration.