BST - LCA
#include <bits/stdc++.h> // Includes common standard libraries (iostream, etc.)#include "BST.h" // Assumed to contain Node class, insertIntoBST, takeinput, levelOrderTraversal, searchNode
using namespace std; // Use the standard namespace
// Note: The Node class, insertIntoBST, takeinput, levelOrderTraversal, and searchNode// are assumed to be defined in "BST.h"./*class Node {public: int data; Node* left; Node* right; Node(int data) { ... }};
Node* insertIntoBST(Node* root, int data) { ... }void takeinput(Node* &root) { ... }void levelOrderTraversal(Node* root) { ... }// Assumed searchNode function to get Node* from dataNode* searchNode(Node* root, int key) { if (root == NULL || root->data == key) return root; if (key < root->data) return searchNode(root->left, key); return searchNode(root->right, key);}*/
// Recursive Way (commented out) - Alternative implementation for LCA in BST/*Node* LCAinBST(Node* root, Node* P, Node* Q) { // Base Case: If root is NULL, no LCA found in this path. if(root == NULL) { return NULL; }
// If both P and Q are in the right subtree of root if(root->data < P->data && root->data < Q->data) { return LCAinBST(root->right, P, Q); }
// If both P and Q are in the left subtree of root if(root->data > P->data && root->data > Q->data) { return LCAinBST(root->left, P, Q); }
// If P and Q are on opposite sides of root, or root is P/Q, then root is the LCA. return root;}*/
// Iterative Way - Function to find the Lowest Common Ancestor (LCA) of two nodes P and Q in a BST.// Time Complexity: O(H) where H is the height of the tree.// Space Complexity: O(1)Node* LCAinBST(Node* root, Node* P, Node* Q) { // Iterate until we find the LCA or reach a NULL node (shouldn't happen if P and Q are in tree) while(root != NULL) { // Case 1: Both P and Q are in the RIGHT subtree of the current root. // This means current root's data is smaller than both P and Q. if(root->data < P->data && root->data < Q->data) { root = root->right; // Move to the right child } // Case 2: Both P and Q are in the LEFT subtree of the current root. // This means current root's data is larger than both P and Q. else if (root->data > P->data && root->data > Q->data) { root = root->left; // Move to the left child } // Case 3: Current root is the LCA. // This happens if P and Q are on opposite sides of root, // or if root itself is P or Q. else { return root; // Found the LCA } } // This line should ideally not be reached if P and Q are guaranteed to be in the tree. return NULL; // In case P or Q (or both) were not found or tree is empty.}
int main() { Node* root = NULL; // Initialize root of the BST int d1, d2; // Data values for the two nodes P and Q
cout << "Enter data to create BST : "; takeinput(root); // Build the BST
cout << "Enter the two node values (d1 d2) : "; cin >> d1 >> d2; // Get the values for nodes P and Q
cout << endl << "Level Order Traversal : " << endl; levelOrderTraversal(root); // Print the tree structure (optional, for verification)
// Find the actual Node* pointers for d1 and d2. // Assumes searchNode function is available and returns NULL if not found. Node* P = searchNode(root, d1); Node* Q = searchNode(root, d2);
// Check if both nodes were found before trying to find LCA if (P == NULL || Q == NULL) { cout << "One or both nodes not found in the BST." << endl; return 1; // Exit with error code }
// Find the Lowest Common Ancestor Node* AnsNode = LCAinBST(root, P, Q);
// Print the result if (AnsNode != NULL) { cout << "Lowest Common Ancestor of " << P->data << " & " << Q->data <<" is : "; cout << AnsNode->data << endl; } else { cout << "LCA could not be determined (possibly an issue with tree or nodes)." << endl; }
return 0;}
/*Example Input for tree creation:14 12 28 8 4 10 9 11 19 32 18 17 22 21 24 39 -1
Expected Tree Structure (partial): 14 / \ 12 28 / \ / \ 8 10 19 32 / / \ / \ \ 4 9 11 18 22 39 / \ 17 21 \ 24
Example Scenarios:1. Enter the two node values : 4 11 Expected LCA: 10 (or 8 depending on exact structure) -> Actually 10, then 12, then 14. Path to 4: 14 -> 12 -> 8 -> 4 Path to 11: 14 -> 12 -> 10 -> 11 LCA is 12 (as 4 is left of 12, 11 is left of 12, but 10 is also left of 12) Let's trace LCAinBST(14, 4, 11): - 14: (4<14, 11<14) -> root = 12 - 12: (4<12, 11<12) -> root = 8 - 8: (4<8, 11>8) -> return 8. Output: Lowest Common Ancestor of 4 & 11 is : 8 (Correct, 8 is the split point for 4 and 11)
2. Enter the two node values : 17 24 Expected LCA: 22 Path to 17: 14 -> 28 -> 19 -> 18 -> 17 Path to 24: 14 -> 28 -> 19 -> 22 -> 24 LCA is 19. Let's trace LCAinBST(14, 17, 24): - 14: (17>14, 24>14) -> root = 28 - 28: (17<28, 24<28) -> root = 19 - 19: (17<19, 24>19) -> return 19. Output: Lowest Common Ancestor of 17 & 24 is : 19 (Correct)
3. Enter the two node values : 4 14 Expected LCA: 14 (P is a descendant of Q, Q is a descendant of P is wrong logic here). Q (14) is ancestor of P (4). So LCA is 14. Let's trace LCAinBST(14, 4, 14): - 14: (4<14, 14>=14) -> return 14. (Because 4 is less than 14, but 14 is NOT less than 14). Output: Lowest Common Ancestor of 4 & 14 is : 14 (Correct)*/
1. Binary Search Tree (BST) Recap
- A Binary Search Tree (BST) follows a specific ordering:
- Left child (and its subtree) values are less than the parent.
- Right child (and its subtree) values are greater than the parent.
- This ordering is fundamental for efficiently finding the LCA.
2. Lowest Common Ancestor (LCA) Definition
- The Lowest Common Ancestor (LCA) of two nodes
P
andQ
in a tree is the deepest node that is an ancestor of bothP
andQ
. - A node can be an ancestor of itself.
3. Supporting Components (Briefly)
Node
class: Defines the structure of a BST node (data
,left
,right
).insertIntoBST
,takeinput
,levelOrderTraversal
: Functions (assumed inBST.h
) to build and visualize the tree.searchNode(root, data)
: An assumed function (likely inBST.h
) that takes the treeroot
and adata
value, and returns aNode*
pointer to the node containing thatdata
. (Crucial formain
to getNode* P
andNode* Q
fromd1
andd2
).
4. LCAinBST(Node* root, Node* P, Node* Q)
Function (Iterative Approach)
-
Purpose: To find the LCA of two given nodes
P
andQ
in a BST. -
Core Idea for BSTs:
- If both
P
andQ
are smaller than the currentroot
, the LCA must be in theleft
subtree. - If both
P
andQ
are larger than the currentroot
, the LCA must be in theright
subtree. - If one is smaller and the other is larger (or one of them is the current
root
), then the currentroot
is the LCA. This is because all nodes smaller thanroot
are to its left, and all larger nodes are to its right. So, ifP
andQ
“straddle” theroot
, orroot
itself is one of them,root
is the meeting point (LCA).
- If both
-
Logic Flow:
- Start a
while
loop that continues as long asroot
is notNULL
. - Condition 1: Both P and Q are smaller than current
root
:if (root->data < P->data && root->data < Q->data)
:- This means
root
is too large to be the LCA (bothP
andQ
are on its right). The LCA must be further down in theright
subtree. - Move
root = root->right;
- Condition 2: Both P and Q are larger than current
root
:else if (root->data > P->data && root->data > Q->data)
:- This means
root
is too small to be the LCA (bothP
andQ
are on its left). The LCA must be further down in theleft
subtree. - Move
root = root->left;
- Condition 3:
root
is the LCA:else
: Thiselse
block is executed when neither of the above conditions is true. This implies:P->data <= root->data <= Q->data
(orQ->data <= root->data <= P->data
)- OR
root
isP
itself - OR
root
isQ
itself.
- In any of these scenarios, the current
root
is the LCA. Returnroot;
- Start a
-
Recursive Way (Commented out in code): Follows the same logic but uses recursive calls instead of a
while
loop. The base case isif(root == NULL) return NULL;
.
5. Complexity Analysis
- Time Complexity:
O(H)
H
is the height of the BST. In each step of the iteration, we move down one level of the tree towards the LCA.- Best Case (Balanced BST):
H = log N
. So,O(log N)
. - Worst Case (Skewed BST/Linear Chain):
H = N
. So,O(N)
.
- Space Complexity:
O(1)
for the iterative approach, as it uses a constant amount of extra space. (For the recursive approach, it would beO(H)
due to the call stack).