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:
[2, 105]
.-109 <= Node.val <= 109
Node.val
are unique.p != q
p
and q
will exist in the tree.This problem asks to find the lowest common ancestor (LCA) of two nodes, p
and q
, in a binary tree. The LCA is the lowest node in the tree that has both p
and q
as descendants (a node is considered a descendant of itself).
The most efficient approach is a recursive post-order traversal of the binary tree. The core idea is as follows:
Base Case: If the current node (root
) is null
, p
, or q
, we return the current node. This handles the cases where we've reached the end of a branch or found one of the target nodes.
Recursive Calls: We recursively call the lowestCommonAncestor
function on the left and right subtrees. This explores both sides of the tree to see if p
and q
are present in each subtree.
Result Combination:
left
and right
) return non-null
nodes, it means p
and q
are found in different subtrees. Therefore, the current node (root
) is their LCA, and we return it.null
node, it implies that one of p
or q
is in that subtree. We return the non-null
result (the node found in that subtree).null
, it means neither p
nor q
is in the current node's subtree, so we return null
.The code examples in various languages demonstrate the recursive approach. They all follow the same logic described above. Note the slight variations in syntax and how null
(or None
) is handled in different languages. The comments in the code highlight the key steps.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
def lowestCommonAncestor(self, root, p, q):
if not root or root == p or root == q: # Base case
return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if left and right: # p and q are in different subtrees
return root
return left if left else right # return the one that is not null
The other languages' code examples follow a similar structure, adapting the syntax and null handling to their respective language conventions. The core recursive logic remains consistent across all implementations.