Skip to content

Lowest Common Ancestor

The lowest common ancestor of two nodes is the deepest node that has both of them as descendants. The obvious approach — find the path to each node, then compare the paths — works and costs O(n)O(n) time plus O(h)O(h) space for the two paths.

The elegant approach is six lines, and it turns on one observation:

If the left subtree reports back “I found one of them” and the right subtree reports back “I found one of them”, then the two nodes are split across this node — so this node is the LCA.

If only one side reports back, the LCA is somewhere in that side, so pass the report upward unchanged. That is the whole algorithm.

  • The general-tree recursion, and precisely what its return value means.
  • Why the BST version collapses to a loop with O(1)O(1) space.
  • The deepest-leaves variant, where the recursion returns a pair.
  • What changes when nodes are not guaranteed to exist, or parent pointers are available.
  • Three real LeetCode problems solved in the browser: 236, 235, 1123.

The split case: p and q are on opposite sides, so the node where both recursive calls return non-None is the answer.

treeLC 236: the LCA is the node whose two subtrees each return a hitgeneral tree · O(n) time, O(h) space
657243018
returnsNone
bubbleNeither subtree of 6 contains a target, so 6 returns None.
1/10

Node 2 is the first node where the left call and the right call both come back non-None, so it is the LCA. Above it, node 5 receives a hit from one side only and passes it up unchanged -- which is what 'return left or right' means: 'I found something below me, but not the meeting point'.

And the ancestor case, which is where the algorithm’s guarantee earns its keep:

treeWhen p is an ancestor of q: the walk returns 5 without ever visiting 4the guarantee is load-bearing
65found7243018
node5
found5 is one of the two targets. Return it upward — the recursion does not need to search below a target, because any node below it would have this node as its ancestor anyway.
1/6

Node 5 is p, so it returns itself on sight and its whole subtree -- including q at node 4 -- is never examined. That is correct only because the problem guarantees both nodes exist in the tree. Remove that guarantee and this answer is wrong for a q that is not present.

lca_general.py
def lowest_common_ancestor(root, p, q):
    if not root or root is p or root is q:
        return root                 # found one of them, or ran out of tree
 
    left = lowest_common_ancestor(root.left, p, q)
    right = lowest_common_ancestor(root.right, p, q)
 
    if left and right:
        return root                 # p and q are on opposite sides -> this is the LCA
    return left or right            # both are in one side (or neither is here)

O(n)O(n) time, O(h)O(h) space for the recursion.

In a BST the ordering tells you which way to go, so no recursion is needed at all:

lca_bst.py
def lca_bst(root, p, q):
    while root:
        if p.val < root.val and q.val < root.val:
            root = root.left          # both smaller: LCA is in the left subtree
        elif p.val > root.val and q.val > root.val:
            root = root.right         # both larger: LCA is in the right subtree
        else:
            return root               # they split here, or one IS this node
    return None

O(h)O(h) time, O(1)O(1) space. The else covers both “the values straddle this node” and “this node is p or q” — in either case you have found the split point and can stop.

ApproachTimeSpaceNeeds
General tree recursionO(n)O(n)O(h)O(h)Nothing
BST descentO(h)O(h)O(1)O(1)The BST ordering
Path comparison (root-to-node lists)O(n)O(n)O(h)O(h)Nothing
Parent pointers (LC 1650)O(h)O(h)O(1)O(1)node.parent
Binary liftingO(logn)O(\log n)/query after O(nlogn)O(n \log n) prepO(nlogn)O(n \log n)Many queries, static tree
Euler tour + sparse tableO(1)O(1)/query after O(nlogn)O(n \log n) prepO(nlogn)O(n \log n)Many queries, and the constant matters

The general recursion is O(n)O(n) and not O(h)O(h) because it has no ordering to steer by — it must be prepared to search the whole tree. It often stops early in practice (the ancestor case above visits 5 of 9 nodes), but the bound is every node, and a tree where p and q sit in the last subtree explored achieves it.

Choose on the query count: one query, use the recursion; a BST, use the descent (O(1)O(1) space, and it fits on a whiteboard); many queries on a static tree, preprocess with binary lifting.

Split case — lca(root, p=7, q=4) on [3,5,1,6,2,0,8,null,null,7,4]. Post-order, so the deepest answers resolve first.

nodeleft callright callreturnsmeaning
6NoneNoneNoneneither node is here
77root is p, return on sight
44root is q, return on sight
2742both sides hit → this is the LCA
5None (from 6)22one side hit → pass it up unchanged
0, 8None
1NoneNoneNonethe whole right subtree is empty of both
32None2one side hit → pass up; the answer surfaces

LCA = 2. Note that node 2’s return root and node 5’s return left or right are doing different jobs with the same value: node 2 decided, node 5 merely relayed.

Ancestor case — lca(root, p=5, q=4). Visits, in order: 3, 5, 1, 0, 8. Then it stops.

  • Node 4 is never visited. Node 5 is p, so it returns itself immediately and its entire subtree — including q — goes unexamined.
  • That is correct only because both nodes are guaranteed present. If q were not in the tree, this returns 5 for a question that has no answer. The fix, if the guarantee is dropped, is to search the whole tree and count how many of p and q were actually found before trusting the result.
  • The right subtree is still fully walked (1, 0, 8) even though the answer was already determined on the left. The recursion cannot know that, which is another way of seeing why the bound is O(n)O(n).

Deepest-leaves variant on the same tree (LC 1123): depths tie at node 2 (leaves 7 and 4 are both at depth 3), so dfs returns (depth, node 2) and the tie propagates upward as the answer — 2 again, for an entirely different reason.

LC 1123 and LC 865 ask for the LCA of all the deepest leaves, which you do not know in advance. The trick is to have the recursion return two things at once: the subtree’s depth, and the LCA of the deepest leaves within it.

lca_deepest_leaves.py
def lca_deepest_leaves(root):
    def dfs(node):
        """Return (depth of this subtree, LCA of its deepest leaves)."""
        if not node:
            return 0, None
        left_depth, left_lca = dfs(node.left)
        right_depth, right_lca = dfs(node.right)
 
        if left_depth == right_depth:
            return left_depth + 1, node      # TIE -> this node is the meeting point
        if left_depth > right_depth:
            return left_depth + 1, left_lca  # deepest leaves are all on the left
        return right_depth + 1, right_lca
 
    return dfs(root)[1]

The tie is the insight: if both sides are equally deep, the deepest leaves live on both sides, so their common ancestor must be this node. If one side is strictly deeper, all the deepest leaves are in it, and its answer propagates upward unchanged.

This is a cousin of the split-brain idea from Tree DFS: return a summary the parent can use, while separately determining an answer that is complete at this node.

VariantWhat the recursion returnsCanonical problem
General binary treeNone, the LCA, or p/q236
BSTNothing — a loop suffices235
Parent pointers availableNothing — two-pointer walk upward1650 (Premium)
Deepest leaves(depth, lca) pair1123 · 865
Nodes may not existAdd explicit found-flags1644
LCA of many nodesFold the pairwise LCA across the set1676
Distance between two nodesLCA, then depth arithmetic1740

LC 236 — Lowest Common Ancestor of a Binary Tree · Medium

Section titled “LC 236 — Lowest Common Ancestor of a Binary Tree · Medium”

Problem. Given the root of a binary tree and two nodes p and q (both guaranteed to exist in the tree), return their lowest common ancestor. A node may be a descendant of itself.

Constraints. 2 <= number of nodes <= 10^5, all Node.val are unique, p != q, and both exist in the tree.

Examples. For [3,5,1,6,2,0,8,null,null,7,4]: LCA of 5 and 1 is 3 · LCA of 5 and 4 is 5 (a node can be its own descendant)

Editorial — approach, complexity, follow-ups

One post-order traversal. Each call answers “did you find p or q below you, and if both, where did they meet?”.

Time O(n)O(n) — every node visited at most once. Space O(h)O(h).

The four test cases cover the distinct shapes:

  • (5, 1) gives 3 — a genuine split at the root.
  • (5, 4) gives 5p is an ancestor of q, so p is the answer. This is where “a node may be a descendant of itself” matters, and where the early return root earns its place.
  • (6, 4) gives 5 — both in the left subtree, split at 5.
  • (7, 4) gives 2 — deep in the tree, showing the answer need not be near the root.

Follow-ups you should expect: “What if p or q might not exist (LC 1644)?” — as above. “With parent pointers (LC 1650)?” — walk up from each node; the elegant trick is two pointers that switch to the other node’s start when they hit the root, meeting at the LCA in O(h)O(h) and O(1)O(1) space — the same idea as the linked-list-intersection problem. “Distance between two nodes?” — depth(p) + depth(q) - 2 * depth(lca). “Many queries on a static tree?” — binary lifting or Tarjan’s offline LCA. “LCA of k nodes (LC 1676)?” — fold the pairwise LCA across them, since LCA is associative.

LC 235 — Lowest Common Ancestor of a Binary Search Tree · Medium

Section titled “LC 235 — Lowest Common Ancestor of a Binary Search Tree · Medium”

Problem. Same question, but the tree is a BST. Return the LCA of p and q.

Constraints. 2 <= number of nodes <= 10^5, unique values, p != q, both exist in the tree.

Examples. For [6,2,8,0,4,7,9,null,null,3,5]: LCA of 2 and 8 is 6 · LCA of 2 and 4 is 2

Editorial — approach, complexity, follow-ups

The LCA is the first node where the two values stop agreeing about direction. While both are smaller, both live left; while both are larger, both live right. The moment they disagree — or one equals the current node — you are standing on the split point.

Time O(h)O(h). Space O(1)O(1).

Compare this with LC 236: the general version must explore both subtrees because it has no way to know where p and q are. The BST version never explores a subtree it does not need, which is what turns O(n)O(n)/O(h)O(h) into O(h)O(h)/O(1)O(1).

The test cases cover each branch:

  • (2, 8) gives 6 — immediate split at the root.
  • (2, 4) gives 2p is an ancestor; the else catches p.val == root.val.
  • (3, 5) gives 4 — both descend left, then split deeper.
  • (0, 5) gives 2 — split at an internal node.

Follow-ups you should expect: “Recursive version?” — same three conditions, recursing instead of looping; O(h)O(h) space for no gain. “What if the BST is unbalanced?” — O(h)O(h) becomes O(n)O(n). “What if p or q might not be present?” — the descent still terminates but you must verify both exist, costing an extra O(h)O(h) search each. “Why does the BST case not need to know which node is smaller?” — because both comparisons are tested independently, so the code is symmetric in p and q.

LC 1123 — Lowest Common Ancestor of Deepest Leaves · Medium

Section titled “LC 1123 — Lowest Common Ancestor of Deepest Leaves · Medium”

Problem. Return the lowest common ancestor of the deepest leaves of the tree. If there is a single deepest leaf, the answer is that leaf.

Constraints. 1 <= number of nodes <= 1000, 0 <= Node.val <= 1000, values are unique.

Examples. [3,5,1,6,2,0,8,null,null,7,4] gives 2 (deepest leaves are 7 and 4) · [1] gives 1 · [0,1,3,null,2] gives 2

Editorial — approach, complexity, follow-ups

You cannot identify the deepest leaves before traversing, so compute depth and answer together, bottom-up.

At each node:

  • Equal child depths — the deepest leaves are distributed across both sides, so their lowest common ancestor is this node.
  • One side strictly deeper — every deepest leaf is in that side, so its answer is still the answer here.

Time O(n)O(n), one pass. Space O(h)O(h).

The base case return 0, None handles both the empty tree and, importantly, leaves: at a leaf both children return (0, None), the depths tie, and the leaf returns (1, itself) — correctly making a lone deepest leaf its own answer. [1] returning 1 confirms this.

[0,1,3,null,2] returning 2 is the single-deepest-leaf case in a larger tree: node 2 is the only node at the maximum depth, so it is its own LCA.

The naive alternative is two passes — find the max depth, collect the leaves at that depth, then fold LC 236’s pairwise LCA over them. That is O(n)O(n) per LCA call and O(n2)O(n^2) overall in the worst case. The paired return does it in one pass.

This is also LC 865 (Smallest Subtree with all the Deepest Nodes) word for word — the same problem under a different title, so solving one gives you both.

Follow-ups you should expect: “Return the depth as well?” — it is already in the pair. “What if you wanted the LCA of all leaves at a given depth?” — same recursion with the target depth passed down. “Iteratively?” — BFS to find the deepest level, then fold pairwise LCAs; more code and worse complexity.

Generated from the problem database, so each entry carries its sheet membership and reported companies. Tick them off as you go — progress is saved in this browser, and the Export button writes it to a file you can keep.

6 problems
0 easy6 medium0 hard

Work down the ladder. Tick each problem off as you go — progress is saved in this browser, and the Export button in the filter bar writes it to a file you can keep.

They askWhat they’re checkingThe answer
“What does your recursion return?”Whether you understand itNone if neither node is below; the LCA if both are; p or q if only one is
“Why can you return early on finding p?”Awareness of the assumptionBecause both nodes are guaranteed present, so if p is an ancestor of q then p is the LCA
“What if a node might be missing?”The boundaryThe early return breaks; do a full traversal counting finds and only accept the candidate at count 2 (LC 1644)
“Why is the BST version better?”Using the structureThe ordering says which subtree to enter, so you never explore both — O(h)O(h) and O(1)O(1) space
is or ==?”CareIdentity — values may repeat, and LeetCode hands you node objects
“Distance between two nodes?”Compositiondepth(p) + depth(q) - 2 * depth(lca)
“Thousands of queries on one tree?”BreadthBinary lifting (O(logn)O(\log n) per query after O(nlogn)O(n \log n) prep), or Tarjan’s offline LCA with union-find
  • One node is an ancestor of the other(5, 4) gives 5; the case the early return handles.
  • The LCA is the root — nodes in opposite subtrees.
  • The LCA is deep(7, 4) gives 2; the answer need not be near the root.
  • Two-node tree — the minimum size LC 236 allows.
  • Single node (LC 1123) — it is its own answer.
  • Single deepest leaf — returns that leaf, not its parent.
  • Duplicate values — compare by identity; LC 236 forbids them but the habit matters.
  • Node not in the tree — breaks the standard solution; LC 1644 is the variant that handles it.
  • Degenerate chainO(h)=O(n)O(h) = O(n); also a recursion-depth risk at large n.
pch.quizTag Lowest common ancestor — self-check
  1. What does the recursion's return value mean?

    pch.quizShowAnswer

    B — Three things depending on the subtree: None if neither node is in it, p or q itself if only one is, and the LCA if both are — Being able to state all three cases is what separates understanding this algorithm from having memorised six lines. `if left and right: return root` is case three; `return left or right` relays cases one and two.

  2. `if root is p or root is q: return root` stops without checking whether the other node is below. Why is that correct?

    pch.quizShowAnswer

    B — Because the problem guarantees both nodes exist in the tree — so if p is an ancestor of q, p is the LCA and no confirmation is needed — In the ancestor dry run, node 4 is never visited. Drop the existence guarantee (LC 1644) and this returns an answer for a question with none — then you must search the whole tree and count the hits.

  3. Why `root is p` rather than `root.val == p.val`?

    pch.quizShowAnswer

    B — Because trees may contain duplicate values and LeetCode passes actual node objects — comparing values finds the wrong node whenever a duplicate sits above the real one — LC 236 guarantees unique values so `==` passes there, which is exactly why the habit goes unpunished until a variant allows duplicates.

  4. In a BST, why is the iterative descent better than the general recursion?

    pch.quizShowAnswer

    B — The ordering tells you which way to go, so it is O(h) time and O(1) space with no recursion — and the `else` branch covers both 'the values straddle this node' and 'this node is p or q' — The general recursion must be prepared to search everything, hence O(n). With an ordering to steer by there is nothing to search — you walk one path down.

  5. LC 1123 asks for the LCA of all deepest leaves, which you do not know in advance. What is the trick?

    pch.quizShowAnswer

    B — Return two things per node — subtree depth and the LCA of its deepest leaves — and treat a depth TIE as 'this node is the meeting point' — A tie means the deepest leaves live on both sides, so their common ancestor is this node; a strictly deeper side propagates its own answer upward. It is the split-brain idea from the Tree DFS page: return a summary the parent can use, decide separately what is complete here.

  6. You must answer 10^5 LCA queries on one fixed tree. What changes?

    pch.quizShowAnswer

    B — Preprocess: binary lifting gives O(log n) per query after O(n log n) setup; break-even is around log n queries — O(nq) is 10^10 on a 10^5-node tree. Caching helps only if queries repeat. The jump table is the standard answer, and Euler tour + sparse table gets O(1) per query if the constant matters.

  • Cue — “lowest common ancestor”, or any “where do these two paths meet” question in a tree.
  • General tree — return root if it is p or q; recurse both sides; both sides non-None → this node is the LCA; otherwise return left or right.
  • Return value means three thingsNone (neither here), p/q (only one here), the LCA (both here).
  • BST — descend: both smaller → left, both larger → right, else this is the split point. O(h)O(h) time, O(1)O(1) space.
  • Compare identity (is), never values — duplicates break value comparison.
  • The existence guarantee is load-bearing — returning p on sight is only valid because q is known to be in the tree (contrast LC 1644).
  • Deepest leaves (LC 1123) — return (depth, lca); a depth tie means this node is the answer.
  • Many queriesbinary lifting, O(logn)O(\log n) per query after O(nlogn)O(n \log n) preprocessing.
  • If both subtrees report a find, this node is the LCA. If only one does, pass its report upward. Six lines, O(n)O(n) time, O(h)O(h) space.
  • The return value means three different things — None, the LCA, or one of the targets. Be able to say which.
  • The early return root depends on both nodes existing. Remove that guarantee and you need explicit found-counting.
  • A BST replaces the recursion with a loop: descend while both values agree on direction, stop when they split. O(h)O(h) time, O(1)O(1) space.
  • Compare by identity, not by value.
  • For deepest leaves, return a (depth, lca) pair and let a depth tie identify the meeting point — one pass instead of O(n2)O(n^2).
  • With parent pointers it becomes the linked-list intersection two-pointer trick.

Next: Tree Construction from Traversals — rebuilding a tree from its preorder and inorder sequences, and why one traversal is never enough.

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading