Input: root = [6,2,8,0,4,7,9,null,null,3,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.
classSolution:
deflowestCommonAncestor(self, root: Optional[TreeNode], p: TreeNode, q: TreeNode) -> Optional[TreeNode]:
ifnot root:
returnNoneif p.val < root.val and q.val < root.val:
return self.lowestCommonAncestor(root.left, p, q) # Both p and q are in the left subtreeif p.val > root.val and q.val > root.val:
return self.lowestCommonAncestor(root.right, p, q) # Both p and q are in the right subtreereturn root # We have found the split point, i.e., the LCA node.
⏰ Time complexity: O(log n) if the tree is balanced; O(n) if the tree is skewed.
🧺 Space complexity: O(h) where h is the height of the tree (which can be O(n) in the worst case).
What if it is Not Guaranteed That P and Q Will Be in Tree?#
Then we can add isNodePresent function and call it before calling our LCA function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Function to check if a given node is present in a binary tree or notpublicstaticbooleanisNodePresent(Node root, Node node) {
// base caseif (root ==null) {
returnfalse;
}
// if the node is found, return trueif (root == node) {
returntrue;
}
// return true if a given node is found in the left or right subtreereturn isNodePresent(root.left, node) || isNodePresent(root.right, node);
}
Driver code:
1
2
3
4
5
6
boolean pPresent = isNodePresent(root, p);
if (!pPresent) returnnull;
boolean qPresent = isNodePresent(root, q);
if (!qPresent) returnnull;
return lowestCommonAncestor(root, p, q);
classSolution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
while (root !=null) {
if (p.val< root.val&& q.val< root.val) {
root = root.left; // Both p and q are in the left subtree } elseif (p.val> root.val&& q.val> root.val) {
root = root.right; // Both p and q are in the right subtree } else {
// We have found the split point, i.e., the LCA node.return root;
}
}
returnnull; // Only if root is null (shouldn't happen in valid BST) }
}
1
2
3
4
5
6
7
8
9
10
classSolution:
deflowestCommonAncestor(self, root: Optional[TreeNode], p: TreeNode, q: TreeNode) -> Optional[TreeNode]:
while root:
if p.val < root.val and q.val < root.val:
root = root.left # Both p and q are in the left subtreeelif p.val > root.val and q.val > root.val:
root = root.right # Both p and q are in the right subtreeelse:
return root # We have found the split point, i.e., the LCA node.returnNone# Only if root is null (shouldn't happen in valid BST)