Lowest Common Ancestor of a Binary Tree
You are given the root of a binary tree (not necessarily a search tree) and two nodes p and q that are both in the tree. Return their lowest common ancestor: the deepest node that has both p and q in its subtree. A node counts as part of its own subtree, so if p lies below q, the answer is q itself.
In the tests, the tree is written as a level-order array where null marks a missing child, and p and q are given by their values (all values are distinct). Your function receives the actual node objects and must return a node; the judge checks the value of the node you return.
Example 1
Example 2
Example 3
Constraints
2 <= number of nodes <= 105-109 <= Node.val <= 109; all values are distinct- p and q are both in the tree (they may be the same node)
Pattern Path · Trees: DFS · problem 87 of 169
p or q?p or q if it finds one in the subtree (or the answer, once known), and null otherwise. If the current node is p or q, return it right away.p and q are on different sides, so the current node is the answer. If only one side is non-null, pass that result up.Start simple
Record the root-to-node path for p and for q with two searches, then walk both paths together until they diverge: O(n) time but two passes and O(n) extra lists.
The key idea
Do a single post-order DFS where each call returns one of three things for its subtree: null (neither target found), a target node (one of them found), or the final answer.
- If the current node is
null,porq, return it immediately. - Otherwise recurse into both children.
- If both children report a node,
pis in one subtree andqin the other, so this node is the deepest place where they meet: return it. - If only one side is non-null, return that side: it either holds one target that still needs its partner, or an answer found deeper down that should bubble up unchanged.
Why is returning early at p safe even when q is below p? Because then nothing else in the tree reports a node, so p bubbles up all the way to the root and is the correct answer. Every node is visited at most once.
Complexity
- Time:
O(n) - Space:
O(h) for the recursion, h = tree height
Python
class Solution:
def lowestCommonAncestor(self, root, p, q):
if root is None or root is p or root is q:
return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if left and right:
return root # p and q are on different sides
return left or right # pass up whatever was found (or None)
JavaScript
var lowestCommonAncestor = function(root, p, q) {
if (root === null || root === p || root === q) return root;
const left = lowestCommonAncestor(root.left, p, q);
const right = lowestCommonAncestor(root.right, p, q);
if (left && right) return root; // p and q are on different sides
return left || right; // pass up whatever was found (or null)
};
What the interviewer is checking
This tests whether you can design a recursive function whose return value carries just enough information upward, and reason about the case where one node is an ancestor of the other.
Mistakes to avoid
- Using BST ordering (comparing values), which only works for search trees.
- Not handling the case where one target is an ancestor of the other.
- Comparing values instead of node identity when duplicates could exist (here they do not, but the habit matters).
- Deep, skewed trees can overflow the recursion stack; an iterative parent-pointer approach avoids that.
Follow-ups to expect
- What if the tree is a binary search tree? (Walk down using value comparisons.)
- What if p or q might be missing from the tree? (Count found targets during DFS.)
- Many queries on the same tree? (Binary lifting or Euler tour + RMQ.)
In real software
Version-control systems find the merge base of two branches as a lowest common ancestor, and DOM libraries, file systems and org charts use it to find the closest shared container or manager.