BST - InOrder Predecessor Successor
#include <bits/stdc++.h> // Includes common standard libraries (iostream, utility for pair)#include "BST.h" // Assumed to contain Node class, insertIntoBST, takeinput, levelOrderTraversal
using namespace std; // Use the standard namespace
// Note: The Node class, insertIntoBST, takeinput, and levelOrderTraversal// 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) { ... }*/
// Function to find the in-order predecessor and successor of a given 'key' in a BST.// Returns a pair: {predecessor, successor}. Returns -1 if not found.// Time Complexity: O(H) where H is the height of the tree.// Space Complexity: O(1)pair<int, int> predecessorSuccessor(Node* root, int key) { // Initialize predecessor and successor with -1 (or other indicator for not found) int pred = -1; int succ = -1;
// --- Phase 1: Traverse to find the key node and capture ancestor candidates --- Node* KeyNode = root; // Start search from root
// Important: The loop condition `KeyNode->data != key` implicitly assumes 'key' is present. // A robust version would be `while(KeyNode != NULL && KeyNode->data != key)`. while(KeyNode != NULL && KeyNode->data != key) { if(KeyNode->data > key) { // If current node's data is greater than key: // It could be a successor. Store it and go left to find a smaller successor. succ = KeyNode->data; KeyNode = KeyNode->left; } else { // KeyNode->data < key // If current node's data is smaller than key: // It could be a predecessor. Store it and go right to find a larger predecessor. pred = KeyNode->data; KeyNode = KeyNode->right; } }
// After the loop, KeyNode points to the node with 'key' (if found) or NULL (if not found). // If key was not found, the `pred` and `succ` from the path are the closest values. // If KeyNode is NULL here, it means the key was not found. if (KeyNode == NULL) { return {pred, succ}; // Return candidates from path if key not found }
// --- Phase 2: Refine predecessor and successor based on KeyNode's children ---
// Find Actual Predecessor: // If the KeyNode has a left subtree, the predecessor is the maximum value in that subtree. Node* leftTree = KeyNode->left; while(leftTree != NULL) { pred = leftTree->data; // Update pred with current node's data leftTree = leftTree->right; // Keep going right to find the maximum in left subtree }
// Find Actual Successor: // If the KeyNode has a right subtree, the successor is the minimum value in that subtree. Node* rightTree = KeyNode->right; while(rightTree != NULL){ succ = rightTree->data; // Update succ with current node's data rightTree = rightTree->left; // Keep going left to find the minimum in right subtree }
return {pred, succ}; // Return the found predecessor and successor}
int main() { Node* root = NULL; // Initialize root of the BST int key; // The key element for which to find pred/succ
cout << "Enter data to create BST : "; takeinput(root); // Build the BST
cout << "Enter the value of key element : "; cin >> key; // Get the key from the user
cout << endl << "Level Order Traversal : " << endl; levelOrderTraversal(root); // Print the tree structure (optional, for verification)
// Call the function to find predecessor and successor pair<int, int> ans = predecessorSuccessor(root, key);
// Print the results cout << "Predecessor : " << ans.first << endl; cout << "Successor : " << ans.second << endl;
return 0;}
/*Example Input for tree creation (from problem description):9 6 2 8 11 19 7 12 25 -1
Expected Tree Structure (approximate): 9 / \ 6 11 / \ \ 2 8 19 / / \ 7 12 25
Example Scenarios:1. Enter key: 9 (Root node, 2 children) - Pred: max in left subtree (8) - Succ: min in right subtree (11) Output: Predecessor : 8, Successor : 11
2. Enter key: 7 (Leaf node) - Pred: 6 (ancestor) - Succ: 8 (ancestor) Output: Predecessor : 6, Successor : 8
3. Enter key: 2 (Leftmost node, no left subtree) - Pred: -1 (no smaller element) - Succ: 6 (ancestor) Output: Predecessor : -1, Successor : 6
4. Enter key: 25 (Rightmost node, no right subtree) - Pred: 19 (ancestor) - Succ: -1 (no larger element) Output: Predecessor : 19, Successor : -1
5. Enter key: 10 (Key not in tree, but between 9 and 11) - Pred: 9 (closest smaller) - Succ: 11 (closest larger) Output: Predecessor : 9, Successor : 11 (This depends on the corrected `while` loop, otherwise might crash)*/
1. Binary Search Tree (BST) Recap
- A Binary Search Tree (BST) maintains a specific ordering: for any node, all values in its left subtree are less than its data, and all values in its right subtree are greater than its data.
2. Predecessor and Successor Definitions
For a given key
in a BST:
-
Predecessor: The largest node value in the BST that is smaller than the
key
.- If the
key
node exists and has a left subtree, its predecessor is the maximum value in its left subtree (the rightmost node in the left subtree). - If the
key
node exists but has no left subtree, its predecessor is the closest ancestor that is smaller than it. This ancestor is found by looking at the path from the root to thekey
node: it’s the last ancestor for which thekey
node (or its ancestors) was in the right subtree.
- If the
-
Successor: The smallest node value in the BST that is larger than the
key
.- If the
key
node exists and has a right subtree, its successor is the minimum value in its right subtree (the leftmost node in the right subtree). - If the
key
node exists but has no right subtree, its successor is the closest ancestor that is larger than it. This ancestor is found by looking at the path from the root to thekey
node: it’s the last ancestor for which thekey
node (or its ancestors) was in the left subtree.
- If the
-
Handling Key Not Found: If the
key
itself is not present in the BST, thepred
will be the greatest value less thankey
that is in the tree, andsucc
will be the smallest value greater thankey
that is in the tree. The algorithm often works correctly for this scenario too.
3. predecessorSuccessor(Node* root, int key)
Function
-
Purpose: To find both the in-order predecessor and in-order successor of a given
key
in the BST. -
Approach (Two-Phase Strategy):
-
Phase 1: Search for the
key
node and capture potential ancestors forpred
andsucc
.- Initialize
pred = -1
andsucc = -1
. These will store our candidates. - Traverse the tree using
KeyNode
pointer, starting fromroot
. - While
KeyNode->data != key
(and assumingKeyNode
is notNULL
):- If
KeyNode->data > key
: The currentKeyNode->data
is greater thankey
. It’s a candidate for thesuccessor
. Updatesucc = KeyNode->data
, and moveKeyNode = KeyNode->left
to find a potentially smaller successor. - If
KeyNode->data < key
: The currentKeyNode->data
is smaller thankey
. It’s a candidate for thepredecessor
. Updatepred = KeyNode->data
, and moveKeyNode = KeyNode->right
to find a potentially larger predecessor.
- If
- When the loop finishes,
KeyNode
points to the node containingkey
. At this point,pred
andsucc
hold the closest ancestor values if the actualkey
node does not have a left/right subtree.
- Initialize
-
Phase 2: Find actual
pred
andsucc
from theKeyNode
’s subtrees.- Finding Predecessor:
- If
KeyNode->left
is notNULL
: The actual predecessor is the largest element in theKeyNode
’s left subtree. TraverseleftTree = KeyNode->left
and keep movingleftTree = leftTree->right
untilleftTree->right
isNULL
. Setpred = leftTree->data
.
- If
- Finding Successor:
- If
KeyNode->right
is notNULL
: The actual successor is the smallest element in theKeyNode
’s right subtree. TraverserightTree = KeyNode->right
and keep movingrightTree = rightTree->left
untilrightTree->left
isNULL
. Setsucc = rightTree->data
.
- If
- Finding Predecessor:
-
-
Return Value: Returns a
std::pair<int, int>
where.first
ispred
and.second
issucc
. -
Important Note on Code’s
while(KeyNode->data != key)
: This loop implicitly assumes thekey
will always be found in the tree. If thekey
is not present,KeyNode
would eventually becomeNULL
(KeyNode->left
orKeyNode->right
becomingNULL
), leading to a null pointer dereference (KeyNode->data
) and a crash. A robust implementation would bewhile(KeyNode != NULL && KeyNode->data != key)
.
4. Complexity Analysis
- Time Complexity:
O(H)
H
is the height of the BST. The initial search for thekey
takesO(H)
. Finding the min/max in a subtree also takesO(H)
in the worst case (skewed tree), but more precisely,O(log N)
on average andO(depth_of_node)
+O(height_of_subtree)
after finding the node. Effectively, it’s proportional to the height.
- Space Complexity:
O(1)
- This iterative approach uses a constant amount of extra space (a few pointers), making it very space-efficient. (A recursive approach would use
O(H)
for the call stack).
- This iterative approach uses a constant amount of extra space (a few pointers), making it very space-efficient. (A recursive approach would use