Lowest Common Ancestor of a BST — One-Line Guided Walk
Why BST ordering collapses LCA into a one-line traversal, and the iterative version that needs zero extra space.
What you'll learn
- ✓How BST ordering turns LCA into a search
- ✓The recursive one-liner and the iterative version
- ✓Why this problem is strictly easier than LCA on a generic tree
- ✓Edge cases when one node is an ancestor of the other
Prerequisites
- •BST basics — see Binary Trees: Introduction
- •Comfort with tree recursion — see Recursion Fundamentals
LCA on a BST is the gentler twin of LCA on a generic binary tree. On a generic binary tree, finding the LCA requires a careful post-order trick. On a BST, the ordering hands you the answer almost for free: the LCA is the first node whose value sits between the two targets.
The Problem
Given the root of a BST and two nodes p and q (guaranteed to exist), return their lowest common ancestor — the deepest node that has both p and q as descendants. By convention, a node is a descendant of itself, so if p is an ancestor of q, the answer is p.
Intuition
Walk from the root and exploit ordering:
- If both
pandqare less than the current node, the LCA is in the left subtree. - If both are greater, it is in the right subtree.
- Otherwise the current node splits them — it is the LCA.
That third case also covers “one of them equals the current node” because the strict comparisons fall through.
Explanation with Example
Take the BST [6, 2, 8, 0, 4, 7, 9, null, null, 3, 5] with p = 2 and q = 8.
- Start at 6.
p.val = 2 < 6andq.val = 8 > 6. They split here. Return 6.
Now try p = 2, q = 4.
- Start at 6. Both targets are less than 6. Go left.
- At 2.
pequals the current node. Sincep.valis not strictly less and not strictly greater, the condition falls through toreturn node. Answer: 2.
The “node is a descendant of itself” rule is exactly what makes the early termination clean.
6 <-- p=2 < 6 and q=8 > 6 SPLIT -> LCA = 6
/ \
2 8
/ \ / \
0 4 7 9
/ \
3 5
decision rule at each node:
both targets < node.val --> go left
both targets > node.val --> go right
otherwise (split or equal) --> return node Code
def lowestCommonAncestor(root, p, q):
node = root
while node:
if p.val < node.val and q.val < node.val:
node = node.left
elif p.val > node.val and q.val > node.val:
node = node.right
else:
return nodepublic TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
TreeNode node = root;
while (node != null) {
if (p.val < node.val && q.val < node.val) node = node.left;
else if (p.val > node.val && q.val > node.val) node = node.right;
else return node;
}
return null;
}TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
TreeNode* node = root;
while (node) {
if (p->val < node->val && q->val < node->val) node = node->left;
else if (p->val > node->val && q->val > node->val) node = node->right;
else return node;
}
return nullptr;
}Edge Cases
- One node is an ancestor of the other — handled, because the comparison falls through when one value equals the current node.
pandqare the same node — return that node.- A skewed BST that is essentially a sorted linked list — still O(h), where h equals n. Worth flagging in interview if you suspect adversarial input.
- The tree contains duplicates — the standard problem forbids this; if asked, you must define BST strictness first.
Complexity Analysis
Time is O(h) where h is tree height. Balanced BST gives O(log n), skewed BST gives O(n). Space is O(1) for the iterative version and O(h) for the recursive one. The brute-force generic-tree solution remains O(n) time and O(n) space.
If the comparison of “O(h) vs O(n)” feels fuzzy, the Big-O notation post covers how to talk about height-bound complexities cleanly.
How to Explain It in an Interview
State the property first: “In a BST, the LCA of p and q is the first node on the root-to-leaf path whose value lies between them, inclusive.” Then derive the three cases. Mention you could fall back to the generic LCA approach if the BST invariant were broken. Close with the space trade — recursive is shorter, iterative wins on stack frames.
Related Problems
- Lowest Common Ancestor of a Binary Tree (the generic version)
- LCA when nodes may not exist
- LCA with parent pointers
- Validate Binary Search Tree
For deeper coverage of traversals that LCA-style problems lean on, see Binary Tree Traversals.
Wrap up
LCA on a BST is the rare problem where the optimal solution is shorter than the brute force and runs in less memory. The lesson is to always ask: what invariant does the data structure give me? Here, sorted order collapses an entire DFS into a guided walk. Lock in the three-case template and this problem becomes a freebie.
Related articles
- DSA Kth Smallest Element in a BST — Iterative Inorder Early Exit
The inorder traversal trick, the iterative early-exit version, and the follow-up that haunts FAANG interviews.
- DSA Validate Binary Search Tree — Min/Max Bounds DFS
The seductive wrong answer, the correct min/max bounds approach, and how to defend it in an interview.
- DSA Binary Tree Level Order Traversal — Queue Size Snapshot
The queue-with-size BFS pattern, why DFS still works, and how this template extends to zigzag and right-side view.
- DSA Path Sum — DFS With a Running Target
The DFS-with-decrement trick, why the leaf condition is the whole problem, and the variants that build on it.