Lowest Common Ancestor of a Binary Tree⚓︎
Description⚓︎
Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.
According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p
and q
as the lowest node in T
that has both p
and q
as descendants (where we allow a node to be a descendant of itself).”
Example 1:
- 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:
- Input:
root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
- Output:
5
- Explanation:
The LCA of nodes 5 and 4 is 5, since a node can be a descendant of itself according to the LCA definition.
Example 3:
- Input:
root = [1,2], p = 1, q = 2
- Output:
1
Constraints:
- The number of nodes in the tree is in the range
[2, 10^5]
. -10^9 <= Node.val <= 10^9
- All
Node.val
are unique. p != q
p
andq
will exist in the tree.
Solution⚓︎
Recursive Solution⚓︎
Simplified version (See reference (Chinese)):
The main idea behind the algorithm is to recursively search for the two nodes (p
and q
) starting from the root. During the recursion, any node can be in one of the three states:
- The node itself is one of the targets (
p
orq
). - The node is an ancestor of one of the targets, which means one of the targets is located in its left or right subtree.
- The node is the lowest common ancestor (LCA), which means one target is in its left subtree, and the other is in its right subtree.
Steps:
-
Base Case: If the current node is
nullptr
, we returnnullptr
, indicating that neitherp
norq
was found in the current path. -
Check Current Node: If the current node is either
p
orq
, return the current node. This means we have found one of the two nodes we're looking for. -
Divide: Recursively call the function for the left and right children of the current node.
left
will contain the result of the LCA search in the left subtree.right
will contain the result of the LCA search in the right subtree.
-
Conquer:
- If both
left
andright
are non-null, it means we have found bothp
andq
in different subtrees of the current node. Hence, the current node is their LCA. - If both
left
andright
are null, it means neitherp
norq
was found in the subtrees of the current node. Returnnullptr
. - If one of
left
orright
is non-null, return the non-null one. This means we have found eitherp
orq
(but not both) in one of the subtrees.
- If both
Time Complexity:
The time complexity of this algorithm is \(O(N)\), where \(N\) is the number of nodes in the binary tree. This is because, in the worst case, the algorithm needs to visit each node exactly once to check if it's either of the nodes we're looking for or if it's their lowest common ancestor.
Space Complexity:
The space complexity of the algorithm is \(O(H)\), where \(H\) is the height of the binary tree. This space is used by the call stack during the recursion. In the worst case, for a skewed binary tree, the height of the tree can become \(N\) (where the tree is essentially a linked list), making the space complexity \(O(N)\). For a balanced tree, the space complexity would be \(O(\log N)\), where \(\log N\) is the height of the tree.
Iterative Solution⚓︎
Approach Explanation:
- Start from the root node and traverse the tree.
- Until we find
p
andq
both, keep storing the parent pointers in a dictionary. - Once we have found both
p
andq
, we get all the ancestors forp
using the parent dictionary and add to a set calledancestors
. - Similarly, we traverse through ancestors for node
q
. If the ancestor is present in the ancestors set forp
, this means this is the first ancestor common betweenp
andq
(while traversing upwards) and hence this is the LCA node.