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
PandQin a tree is the deepest node that is an ancestor of bothPandQ. - A node can be an ancestor of itself.
3. Supporting Components (Briefly)
Nodeclass: 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 treerootand adatavalue, and returns aNode*pointer to the node containing thatdata. (Crucial formainto getNode* PandNode* Qfromd1andd2).
4. LCAinBST(Node* root, Node* P, Node* Q) Function (Iterative Approach)
-
Purpose: To find the LCA of two given nodes
PandQin a BST. -
Core Idea for BSTs:
- If both
PandQare smaller than the currentroot, the LCA must be in theleftsubtree. - If both
PandQare larger than the currentroot, the LCA must be in therightsubtree. - If one is smaller and the other is larger (or one of them is the current
root), then the currentrootis the LCA. This is because all nodes smaller thanrootare to its left, and all larger nodes are to its right. So, ifPandQ“straddle” theroot, orrootitself is one of them,rootis the meeting point (LCA).
- If both
-
Logic Flow:
- Start a
whileloop that continues as long asrootis notNULL. - Condition 1: Both P and Q are smaller than current
root:if (root->data < P->data && root->data < Q->data):- This means
rootis too large to be the LCA (bothPandQare on its right). The LCA must be further down in therightsubtree. - 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
rootis too small to be the LCA (bothPandQare on its left). The LCA must be further down in theleftsubtree. - Move
root = root->left;
- Condition 3:
rootis the LCA:else: Thiselseblock 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
rootisPitself - OR
rootisQitself.
- In any of these scenarios, the current
rootis the LCA. Returnroot;
- Start a
-
Recursive Way (Commented out in code): Follows the same logic but uses recursive calls instead of a
whileloop. The base case isif(root == NULL) return NULL;.
5. Complexity Analysis
- Time Complexity:
O(H)His 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).