How to Find the Lowest Common Ancestor (LCA) in a Binary Tree
Use a recursive post-order DFS that returns a node when it finds either target, and when both left and right subtrees return non-null values, the current node is the LCA.
Finding the lowest common ancestor (LCA) in a binary tree is a fundamental algorithmic problem frequently encountered in technical interviews and tree-based data processing. This guide examines the optimal recursive solution implemented in the kdn251/interviews repository, providing a complete breakdown of how to find the LCA in a binary tree with O(N) time complexity.
What Is the Lowest Common Ancestor in a Binary Tree?
The lowest common ancestor (LCA) of two nodes p and q in a binary tree is defined as the deepest node that has both p and q as descendants, where a node is considered a descendant of itself. In practical terms, the LCA represents the shared parent node located farthest from the root where the paths to both target nodes diverge.
Recursive DFS Approach to Find LCA in a Binary Tree
The most efficient method to locate the LCA employs a post-order depth-first search (DFS) that traverses from the leaves upward, propagating node references only when targets are discovered.
Algorithm Logic
The recursive strategy follows three distinct phases:
-
Base case handling – If the current
rootisnull, or ifrootmatches eitherporq, immediately returnroot. This captures the scenario where one target is the ancestor of the other. -
Subtree exploration – Recursively invoke the LCA search on
root.leftandroot.rightto probe both subtrees for the target nodes. -
Result combination –
- If both left and right recursive calls return non-
nullvalues, the currentrootrepresents the split point wherepandqreside in different subtrees, makingrootthe LCA. - If only one side returns a non-
nullnode, propagate that result upward, as it contains either one of the targets or the LCA discovered deeper in that branch.
- If both left and right recursive calls return non-
Complexity Analysis
- Time Complexity:
O(N)where N is the number of nodes in the tree. The algorithm visits each node exactly once in the worst case. - Space Complexity:
O(H)where H is the height of the tree, representing the maximum recursion stack depth. In a skewed tree, this degrades toO(N), while a balanced tree requiresO(log N).
Java Implementation from kdn251/interviews
The kdn251/interviews repository provides a clean, production-ready implementation in leetcode/tree/LowestCommonAncestorOfABinaryTree.java. The lowestCommonAncestor method implements the exact recursive post-order strategy described above:
public class LowestCommonAncestorOfABinaryTree {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == p || root == q) {
return root; // base case
}
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (left != null && right != null) { // p on one side, q on the other
return root; // current node is LCA
}
return left == null ? right : left; // propagate non-null result
}
}
This implementation handles all edge cases, including scenarios where one node is the direct ancestor of the other, by returning immediately when a match is found and allowing that result to propagate upward.
Practical Examples
Basic Usage
Consider a binary tree structured as follows:
3
/ \
5 1
/ \ / \
6 2 0 8
/ \
7 4
To find the LCA of nodes 5 and 1:
// Construct the tree
TreeNode root = new TreeNode(3);
root.left = new TreeNode(5);
root.right = new TreeNode(1);
// ... (additional node construction)
LowestCommonAncestorOfABinaryTree solver = new LowestCommonAncestorOfABinaryTree();
TreeNode lca = solver.lowestCommonAncestor(root, root.left, root.right);
System.out.println(lca.val); // Output: 3
To find the LCA of nodes 5 and 4 (where 5 is the ancestor of 4):
TreeNode node4 = root.left.right.right; // Node with value 4
TreeNode lca2 = solver.lowestCommonAncestor(root, root.left, node4);
System.out.println(lca2.val); // Output: 5
Edge Cases
The algorithm gracefully handles several boundary conditions:
- One node is the ancestor of the other – The base case
if (root == null || root == p || root == q)immediately returns the current node when it matches either target. If one target is the ancestor of the other, the ancestor will be encountered first and returned, then propagated upward as the LCA without further searching the descendant's subtree. - One or both nodes missing – If either
porqdoes not exist in the tree, the algorithm returns the existing node (ornullif neither exists), as only the found node propagates upward. - Identical nodes – When
pandqrefer to the same node, the base case triggers on the first encounter and returns that node immediately.
Repository Structure and Related Files
The kdn251/interviews repository organizes this solution across multiple interview preparation tracks. The core implementation resides in the LeetCode section, with identical copies adapted for specific company interview sets:
| Path | Description |
|---|---|
| leetcode/tree/LowestCommonAncestorOfABinaryTree.java | Core recursive solution for LeetCode problem 236. |
| company/amazon/LowestCommonAncestorOfABinaryTree.java | Amazon interview preparation variant. |
| company/facebook/LowestCommonAncestorOfABinaryTree.java | Facebook interview preparation variant. |
| company/linkedin/LowestCommonAncestorOfABinaryTree.java | LinkedIn interview preparation variant. |
| company/twitter/LowestCommonAncestorOfABinaryTree.java | Twitter interview preparation variant. |
All implementations share the identical O(N) time and O(H) space recursive strategy, ensuring consistent performance across different interview contexts.
Summary
- The lowest common ancestor (LCA) in a binary tree is the deepest node where paths to two target nodes diverge.
- The optimal solution uses post-order DFS recursion with
O(N)time complexity andO(H)space complexity. - The algorithm returns immediately when encountering
null,p, orq, then propagates the first non-null result upward until both left and right subtrees return values, identifying the LCA. - The kdn251/interviews repository provides this implementation in
leetcode/tree/LowestCommonAncestorOfABinaryTree.javaand across multiple company-specific interview directories.
Frequently Asked Questions
What is the time complexity of finding the LCA in a binary tree?
The recursive DFS approach runs in O(N) time where N is the number of nodes, because in the worst case it must visit every node in the tree once to locate both target nodes.
Can the LCA algorithm handle cases where one node is the ancestor of the other?
Yes. The base case if (root == null || root == p || root == q) immediately returns the current node when it matches either target. If one target is the ancestor of the other, the ancestor will be encountered first and returned, then propagated upward as the LCA without further searching the descendant's subtree.
What is the space complexity of the recursive LCA solution?
The space complexity is O(H) where H is the height of the tree, representing the maximum recursion stack depth. In a balanced binary tree this is O(log N), but it degrades to O(N) in a skewed tree where each node has only one child.
Where can I find the complete implementation in the kdn251/interviews repository?
The primary implementation resides in leetcode/tree/LowestCommonAncestorOfABinaryTree.java. Identical copies adapted for specific company interview tracks are also available in company/amazon/, company/facebook/, company/linkedin/, company/twitter/, and other directories within the repository.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →