Lowest Common Ancestor LCA in a Binary Tree - Nodes May Not Exist in Tree
Problem
Given the root of a binary tree, return the lowest common ancestor (LCA) of two given nodes, p and q. If either node p or q does not exist in the tree, return null. All values of the nodes in the tree are unique.
Lowest Common Ancestor Definition
Problems related to it - Lowest Common Ancestor Definition
Examples
Example 1:
_______3______
/ \
___5__ ___1__
/ \ / \
6 _2_ 0 8
/ \
7 4
Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
Output: 3
Explanation: The LCA of nodes 5 and 1 is 3.
Example 2:
_______3______
/ \
___5__ ___1__
/ \ / \
6 _2_ 0 8
/ \
7 4
Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 10
Output: null
Explanation: Node 10 does not exist in the tree, so return null.
Follow up
Can you find the LCA traversing the tree, without checking nodes existence?
Solution
Method 1 - Two Pass - Check Nodes Existence
Algorithm
- Check if node
p
andq
exists in Tree. - If either of them don’t exist, return null.
- Else return the LCA we used to to solve - Lowest Common Ancestor of a Binary Tree#Method 1 - Using Post-order Traversal 🏆.
Code
Java
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
boolean l = check(root, p);
boolean k = check(root, q);
if(!l || !k) {
return null;
}
return dfs(root, p, q);
}
private boolean check(TreeNode root, TreeNode p) {
if(root == null) {
return false;
}
if(root == p) {
return true;
}
return check(root.left, p) || check(root.right, p);
}
public TreeNode dfs(TreeNode root, TreeNode p, TreeNode q) {
if(root == null || root == p || root == q) {
return root;
}
TreeNode left = dfs(root.left, p, q);
TreeNode right = dfs(root.right, p, q);
if (left != null && right != null) {
return root;
}
return left != null ? left : right;
}
Method 2 - Recursion with Count of Nodes Found
We can keep track of number of nodes we have found so far. If nodes found is less than 2, then we will return null.
Code
Java
static class Pair(){
TreeNode root;
int count;
public Pair(TreeNode root, int count) {
this.root = root;
this.count = count;
}
}
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
Pair dfsAns = dfs(root, p, q);
if(dfsAns.count < 2){
return null;
}
return dfsAns.root;
}
public Pair dfs(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) {
return null;
}
Pair leftAns = dfs(root.left, p, q);
Pair rightAns = dfs(root.right, p, q);
if(root == p || root == q){
return new Pair(root, 1+leftAns.count + rightAns.count);
}
if(leftAns.root != null && leftAns.root != null){
return new Pair(root, 2);
}
return leftAns.root != null? leftAns: rightAns;
}
Method 3 - By Storing root
to p
and q
Paths using backtracking
This approach is same as Lowest Common Ancestor of a Binary Tree#Method 2 - By Storing root to p and q paths using backtracking
Complexity
- ⏰ Time complexity:
O(n)
. The tree is traversed twice, and paths are compared. - 🧺 Space complexity:
O(n)