| Difficulty | Easy | ||
|---|---|---|---|
| Source | 160 Days of Problem Solving | ||
| Tags |
|
The problem can be found at the following link: Question Link
Given a Binary Search Tree (BST) with unique values and two nodes n1 and n2 (where n1 != n2), find the Lowest Common Ancestor (LCA) of the two given nodes in the BST.
The LCA is the lowest node in the BST that has both n1 and n2 as descendants (a node is also considered its own descendant).
5
/ \
4 6
/ \
3 7
\
8
n1 = 7, n2 = 8
7
The lowest common ancestor of 7 and 8 is 7 itself.
20
/ \
8 22
/ \
4 12
/ \
10 14
n1 = 8, n2 = 14
8
8 is the lowest node that is an ancestor of both 8 and 14.
2
/ \
1 3
n1 = 1, n2 = 3
2
2 is the lowest node that is an ancestor of both 1 and 3.
$(1 \leq \text{Number of Nodes} \leq 10^5)$ $(1 \leq \text{node->data} \leq 10^5)$ $(1 \leq \text{Node Value}, n1, n2 \leq 10^5)$
- Start from the root of the BST.
- If
root->datais greater than bothn1andn2, move to the left subtree. - If
root->datais smaller than bothn1andn2, move to the right subtree. - Otherwise, return
rootas it is the Lowest Common Ancestor (LCA).
- Start at the root node.
- While
rootis notNULL:- If
root->data > max(n1, n2), move left. - If
root->data < min(n1, n2), move right. - Otherwise, root is the LCA, return it.
- If
- Expected Time Complexity:
O(H), whereHis the height of the BST. In a balanced BST,H = log N, so this is efficient. - Expected Auxiliary Space Complexity:
O(1), as no additional data structures are used.
class Solution {
public:
Node* LCA(Node* root, Node* n1, Node* n2) {
while (root && (root->data > max(n1->data, n2->data) || root->data < min(n1->data, n2->data)))
root = (root->data > n1->data) ? root->left : root->right;
return root;
}
};- Base case: If
rootisNULL, returnNULL. - If both
n1andn2are smaller, move left. - If both
n1andn2are greater, move right. - Otherwise, return
root, as it's the LCA.
class Solution {
public:
Node* LCA(Node* root, Node* n1, Node* n2) {
if (!root || root->data == n1->data || root->data == n2->data) return root;
if (root->data > n1->data && root->data > n2->data) return LCA(root->left, n1, n2);
if (root->data < n1->data && root->data < n2->data) return LCA(root->right, n1, n2);
return root;
}
};πΉ Pros: Simple and easy to understand.
πΉ Cons: Uses recursion stack space (O(H)).
- Mimics recursion using an explicit stack.
- Instead of recursive calls, we use a while loop and a stack.
class Solution {
public:
Node* LCA(Node* root, Node* n1, Node* n2) {
stack<Node*> st;
while (root) {
if (root->data > n1->data && root->data > n2->data) root = root->left;
else if (root->data < n1->data && root->data < n2->data) root = root->right;
else return root;
}
return nullptr;
}
};πΉ Pros: Avoids function call stack overflow.
πΉ Cons: Slightly more complex than recursion.
| Approach | β±οΈ Time Complexity | ποΈ Space Complexity | β‘ Method | β Pros | |
|---|---|---|---|---|---|
| Optimized Iterative | π’ O(H) |
π’ O(1) |
Iterative | Best runtime and space efficiency | None |
| Recursive | π’ O(H) |
π‘ O(H) |
Recursion | Simple and readable | Uses recursion stack space |
| Iterative (Stack) | π’ O(H) |
π’ O(1) |
Iterative | Mimics recursion without function calls | Slightly complex implementation |
- β
For best efficiency: Optimized Iterative Approach (
O(1)space,O(H)time). - β For simplicity: Recursive approach.
- β For avoiding recursion depth issues: Iterative (Stack-based) approach.
class Solution {
Node LCA(Node root, Node n1, Node n2) {
while (root != null && (root.data > Math.max(n1.data, n2.data) || root.data < Math.min(n1.data, n2.data)))
root = (root.data > n1.data) ? root.left : root.right;
return root;
}
}class Solution:
def LCA(self, root, n1, n2):
while root and (root.data > max(n1.data, n2.data) or root.data < min(n1.data, n2.data)):
root = root.left if root.data > n1.data else root.right
return rootFor discussions, questions, or doubts related to this solution, feel free to connect on LinkedIn: Any Questions. Letβs make this learning journey more collaborative!
β If you find this helpful, please give this repository a star! β