Lowest Common Ancestor of a Binary Tree

Medium Trees & BST LCA Original on LeetCode

Lowest Common Ancestor of a Binary Tree is a medium trees & bst problem solved with the lca pattern. The best approach, optimal (single postorder pass), runs in O(n) time and O(h) space. Below are 2 approaches in Java, from root-to-node paths up.

Problem

Find the lowest common ancestor of two nodes p and q in a binary tree: the deepest node that has both of them as descendants (a node counts as a descendant of itself).

Examples

Example 1

Input
root = [3, 5, 1, 6, 2, 0, 8, null, null, 7, 4], p = 5, q = 1
Output
3

Example 2

Input
same tree, p = 5, q = 4
Output
5
Why
A node can be its own ancestor.

Constraints

  • The tree has 2 to 10^5 nodes, unique values.
  • p and q both exist in the tree.

Animated walkthrough

A narrated, step-by-step animation that builds the solution from the idea up. Press play, or step through it at your own pace.

Concepts first, then the problem and every approach, step by step.

Space to play or pause · ← → to jump a step · click or drag the bar to seek

Solutions

Try it yourself first. Then compare: each approach lists its idea, the steps, its time and space, and the Java code.

ApproachTimeSpace
Root-to-node pathsO(n)O(n)
Optimal (single postorder pass)O(n)O(h)

1Root-to-node paths

TimeO(n)
SpaceO(n)

Record the path from the root to p and the path to q, then walk both until they differ. The last shared node is the LCA.

  1. path(root, target, list) with backtracking.
  2. Compare paths index by index.
Java
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        List<TreeNode> a = new ArrayList<>(), b = new ArrayList<>();
        path(root, p, a);
        path(root, q, b);
        TreeNode lca = null;
        for (int i = 0; i < Math.min(a.size(), b.size()) && a.get(i) == b.get(i); i++) lca = a.get(i);
        return lca;
    }

    private boolean path(TreeNode n, TreeNode target, List<TreeNode> out) {
        if (n == null) return false;
        out.add(n);
        if (n == target || path(n.left, target, out) || path(n.right, target, out)) return true;
        out.remove(out.size() - 1);
        return false;
    }
}

2Optimal (single postorder pass)

TimeO(n)
SpaceO(h)

Return p or q when you hit them, null when a subtree has neither. If both left and right return non-null, this node splits them, so it is the LCA. Otherwise pass up whichever side found something.

  1. If root is null, p or q, return root.
  2. left = lca(left), right = lca(right).
  3. If both non-null, return root; else return the non-null one.
Java
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || root == p || root == q) return root;
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if (left != null && right != null) return root;
        return left != null ? left : right;
    }
}

Edge cases to test

  • One node is the ancestor of the other
  • p and q in different subtrees of the root

Hints

Hint 1

If p is found in one subtree and q in the other, the current node is the answer.

FAQ

What is the best time complexity for Lowest Common Ancestor of a Binary Tree?

Optimal (single postorder pass) runs in O(n) time and O(h) extra space.

Which pattern does Lowest Common Ancestor of a Binary Tree use?

It is a trees & bst problem that uses the lca pattern. Other problems with the same pattern: Lowest Common Ancestor of a Binary Search Tree.

Is there a brute force solution for Lowest Common Ancestor of a Binary Tree?

Yes. Root-to-node paths takes O(n) time and O(n) space. Record the path from the root to p and the path to q, then walk both until they differ.

Which edge cases should I test for Lowest Common Ancestor of a Binary Tree?

One node is the ancestor of the other; p and q in different subtrees of the root.