Lowest Common Ancestor of a Binary Search Tree medium
Problem Statement
Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST.
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 = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
Output: 6
Explanation: The LCA of nodes 2 and 8 is 6.
Example 2
Input: root = [6,2,8,0,4,7,9,null,null,null,null,null,null,5], p = 2, q = 4
Output: 2
Explanation: The LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition.
Steps and Explanation
The key to solving this problem efficiently lies in leveraging the properties of a Binary Search Tree. In a BST, the left subtree contains nodes with values smaller than the current node, and the right subtree contains nodes with values larger than the current node.
We can use a recursive approach. For each node we encounter:
-
Base Case: If the current node is
null
, we've reached the end of a branch and haven't found eitherp
orq
, so we returnnull
. -
Found Both: If the current node's value is equal to either
p
orq
, and we've already found the other node (either directly or indirectly through recursive calls), then the current node is an ancestor of bothp
andq
. Since we're looking for the lowest common ancestor, we can return the current node immediately. -
One Node Found: If the current node's value is equal to either
p
orq
, but not both, we continue searching recursively. We only need to search one subtree because if one of the nodes is found and its value is within the range defined by the current node (i.e., the other node is not found), the other node must be found within the respective subtree in a BST. -
Neither Node Found: If the current node's value is not equal to
p
orq
, we recursively search the left or right subtree based on the values ofp
andq
relative to the current node's value:- If both
p
andq
are less than the current node's value, we recursively search the left subtree. - If both
p
andq
are greater than the current node's value, we recursively search the right subtree. - If
p
andq
are on opposite sides (one less, one greater), the current node must be their lowest common ancestor, so we return the current node.
- If both
Code (Java)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (left != null && right != null) return root; // p and q are on different sides
return left != null ? left : right; // p and q are on the same side
}
}
Complexity
- Time Complexity: O(N), where N is the number of nodes in the BST. In the worst case, we might traverse the entire tree.
- Space Complexity: O(H), where H is the height of the BST. This is due to the recursive call stack. In the worst case (a skewed tree), H can be equal to N, but in a balanced BST, H is log₂(N).