68. Lowest Common Ancestor of a Binary Tree
Problem Statement
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
Solution
# Definition for a binary tree node.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
# Base Case 1: If root is None, or root is p or q, then root is the LCA
if not root or root == p or root == q:
return root
# Recursively search in left and right subtrees
left_lca = self.lowestCommonAncestor(root.left, p, q)
right_lca = self.lowestCommonAncestor(root.right, p, q)
# Case 1: Both p and q are found in different subtrees
# This means the current root is the LCA
if left_lca and right_lca:
return root
# Case 2: Only one of p or q is found (or both are in the same subtree)
# If left_lca is not None, it means p or q (or both) were found in the left subtree.
# So, left_lca is the LCA.
elif left_lca:
return left_lca
# Case 3: Only right_lca is not None, meaning p or q (or both) were found in the right subtree.
# So, right_lca is the LCA.
else:
return right_lca
Explanation
This problem is a classic recursive tree traversal problem. Unlike a BST, in a general binary tree, we cannot use the node values to guide our search. We must explore both left and right subtrees.
Core Idea: The lowest common ancestor (LCA) of two nodes p
and q
can be found by recursively searching the tree. The LCA is the first node where p
and q
are found in different subtrees, or where one of them is the current node and the other is in one of its subtrees.
Recursive Approach:
-
Base Case:
- If
root
isNone
, it means we've reached the end of a branch without findingp
orq
. ReturnNone
. - If
root
isp
orroot
isq
, it means we've found one of the target nodes. According to the definition, a node can be a descendant of itself, so thisroot
is a potential LCA. Returnroot
.
- If
-
Recursive Calls:
- Recursively call
lowestCommonAncestor
on theroot.left
subtree to findleft_lca
. - Recursively call
lowestCommonAncestor
on theroot.right
subtree to findright_lca
.
- Recursively call
-
Combine Results: After the recursive calls return:
- If both
left_lca
andright_lca
are notNone
: This meansp
was found in one subtree andq
in the other (or vice versa). Therefore, the currentroot
is the LCA. Returnroot
. - If only
left_lca
is notNone
: This means bothp
andq
(or just one of them) were found in the left subtree. So,left_lca
is the LCA. Returnleft_lca
. - If only
right_lca
is notNone
: This means bothp
andq
(or just one of them) were found in the right subtree. So,right_lca
is the LCA. Returnright_lca
. - If both are
None
: Neitherp
norq
were found in the current subtree. ReturnNone
.
- If both
Time and Space Complexity:
- Time Complexity: O(N), where N is the number of nodes in the binary tree. In the worst case, we visit each node once.
- Space Complexity: O(H) for the recursion stack, where H is the height of the tree. In a skewed tree, H can be N (O(N)), and in a balanced tree, H is log N (O(log N)).