235 - Lowest Common Ancestor of a Binary Search Tree
Difficulty: Medium | Pattern: BST Property | Company tags: Amazon, Facebook, LinkedIn
Problem Statement
Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes p and q.
The LCA is defined as the lowest node in the tree that has both p and q as descendants (a node can be a descendant of itself).
Example 1:
Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
Output: 6
Example 2:
Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
Output: 2 (2 is an ancestor of 4)
Approach: Exploit BST Property — O(h), O(1)
Key insight: In a BST, if both p and q are less than root, the LCA is in the left subtree. If both are greater, it's in the right subtree. Otherwise, root is the LCA (they split here, or one of them is the root).
def lowestCommonAncestor(root, p, q):
while root:
if p.val < root.val and q.val < root.val:
root = root.left
elif p.val > root.val and q.val > root.val:
root = root.right
else:
return root
return None
Algorithm Flow
Recursive Version
def lowestCommonAncestor(root, p, q):
if p.val < root.val and q.val < root.val:
return lowestCommonAncestor(root.left, p, q)
if p.val > root.val and q.val > root.val:
return lowestCommonAncestor(root.right, p, q)
return root
Dry Run
BST: [6,2,8,0,4,7,9], p=2, q=4
- root=6: p.val=2 lt 6 AND q.val=4 lt 6 → go left
- root=2: p.val=2 not lt 2 (equal) → else branch → return 2
LCA = 2 ✓ (2 is an ancestor of 4, and it IS p)
p=2, q=8:
- root=6: 2 lt 6 but 8 gt 6 → else → return 6 ✓
Comparison with LC 236 (General Binary Tree LCA)
| LC 235 BST | LC 236 Binary Tree | |
|---|---|---|
| Uses BST property | Yes | No |
| Time | O(h) | O(n) |
| Space | O(1) iterative | O(h) |
The BST solution is simpler because we can navigate directly using value comparisons.
Complexity
- Time: O(h) — O(log n) balanced, O(n) worst case skewed
- Space: O(1) iterative
Key Terms
| Term | Definition in this problem's context |
|---|---|
| BST ordering property | For every node, all left-subtree values are smaller and all right-subtree values are larger — this lets us decide direction using only value comparisons, no need to search both subtrees. |
| Split point | The node where p and q diverge to different subtrees (or one equals the current node) — this is exactly the LCA. |
| Ancestor (self-inclusive) | A node counts as its own ancestor, so if p is an ancestor of q, p itself is the LCA. |
| Iterative descent | Walking down from root using a while loop instead of recursion, achieving O(1) space instead of O(h) call-stack space. |
FAQ
-
Can this be solved without extra space? Yes — the iterative version uses O(1) space since it just reassigns
rootwhile descending; the recursive version uses O(h) stack space instead. -
What if
porqdoesn't exist in the tree? The standard solution assumes both nodes exist in the tree (as per LeetCode's constraints); if that's not guaranteed, you'd first verify both nodes are present via a traversal before trusting the result. -
How would this change if it were a general binary tree instead of a BST? You could no longer use value comparisons to choose a direction; you'd need to search both subtrees recursively and combine results — this is exactly LC 236, which runs in O(n) instead of O(h).
-
What's the follow-up interviewers usually ask? "What if the tree has parent pointers?" — you can then walk up from both nodes similar to finding the intersection point of two linked lists, or solve it without ever touching the root.
-
Does this work correctly when
pandqare the same node? Yes — the loop will keep descending toward that node untilp.val == root.val(and thusq.val == root.val), triggering the else branch and returning that node.
Quick Revision
- Pattern: exploit BST ordering to navigate directly toward the LCA, no need to explore both subtrees.
- If both
p.valandq.valare less thanroot.val, LCA lies in the left subtree — move left. - If both are greater, LCA lies in the right subtree — move right.
- Otherwise (values split across root, or one equals root),
rootis the LCA — stop and return it. - Iterative version: O(h) time, O(1) space — preferred for interviews.
- Recursive version: same O(h) time, but O(h) stack space.
- A node is considered its own ancestor, so if
pis an ancestor ofq, the answer isp. - Contrast with LC 236 (general binary tree): no ordering property, must search both subtrees, O(n) time, O(h) space.
- h = O(log n) for a balanced BST, O(n) for a completely skewed one.
Related Problems
- 236-LowestCommonAncestorOfABinaryTree — the general (non-BST) version of this same problem, requiring a different O(n) approach.
- 98-ValidateBinarySearchTree — shares the core idea of exploiting BST ordering during traversal.
- 108-ConvertSortedArrayToBinarySearchTree — another problem centered on the BST ordering invariant.