BST Inorder Predecessor in DSA C# - Time & Space Complexity
We want to understand how long it takes to find the inorder predecessor in a Binary Search Tree (BST).
This helps us know how the search time grows as the tree gets bigger.
Analyze the time complexity of the following code snippet.
// Find inorder predecessor in BST
TreeNode InorderPredecessor(TreeNode root, TreeNode node) {
if (node.Left != null) {
TreeNode pred = node.Left;
while (pred.Right != null) {
pred = pred.Right;
}
return pred;
}
TreeNode predecessor = null;
TreeNode current = root;
while (current != null) {
if (node.Val > current.Val) {
predecessor = current;
current = current.Right;
} else {
current = current.Left;
}
}
return predecessor;
}
This code finds the inorder predecessor of a given node in a BST by checking left subtree or traversing from root.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Traversing down the tree nodes using while loops.
- How many times: At most, one path from root to a leaf or from node's left child down to rightmost leaf.
As the tree grows taller, the number of steps to find the predecessor grows roughly with the height of the tree.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | Up to 4-5 steps (if tree height ~4) |
| 100 | Up to 7-8 steps (if tree height ~7) |
| 1000 | Up to 10 steps (if tree height ~10) |
Pattern observation: The steps grow with the height of the tree, not directly with total nodes.
Time Complexity: O(h)
This means the time depends on the height of the BST, which is the longest path from root to leaf.
[X] Wrong: "Finding the inorder predecessor always takes O(n) time because we might check all nodes."
[OK] Correct: We only follow one path down the tree, not all nodes, so time depends on tree height, not total nodes.
Understanding this helps you explain how tree shape affects search times and shows you can analyze recursive or loop-based tree operations clearly.
"What if the BST is balanced versus completely skewed? How would the time complexity change?"
Practice
Solution
Step 1: Understand inorder traversal order
Inorder traversal visits nodes in ascending order for BST.Step 2: Define predecessor in inorder traversal
The predecessor is the node visited just before the current node, so it must have the largest value smaller than the current node.Final Answer:
The node with the largest value smaller than the given node -> Option AQuick Check:
Inorder predecessor = largest smaller value [OK]
- Confusing predecessor with successor
- Thinking predecessor is always left child
- Assuming predecessor is the parent node
Solution
Step 1: Check where predecessor lies if left subtree exists
Predecessor is the rightmost node in the left subtree.Step 2: Analyze code snippets
Node predecessor = node.Left; while (predecessor.Right != null) predecessor = predecessor.Right; starts at left child and moves right until no more right child, correctly finding predecessor.Final Answer:
Node predecessor = node.Left; while (predecessor.Right != null) predecessor = predecessor.Right; -> Option DQuick Check:
Left subtree's rightmost node = predecessor [OK]
- Going left repeatedly instead of right in left subtree
- Starting from right child instead of left
- Ignoring the while loop to find rightmost node
20
/ \
10 30
\
15
Solution
Step 1: Locate node 15 and check left subtree
Node 15 has no left child, so predecessor is ancestor smaller than 15.Step 2: Find nearest ancestor smaller than 15
Ancestor 10 is smaller than 15 and is the closest on the path.Final Answer:
10 -> Option BQuick Check:
Predecessor of 15 is 10 [OK]
- Choosing 20 which is larger than 15
- Choosing 15 itself
- Choosing 30 which is right subtree
Node FindPredecessor(Node root, Node node) {
if (node.Left != null) {
Node temp = node.Left;
while (temp.Left != null) {
temp = temp.Left;
}
return temp;
}
Node predecessor = null;
Node current = root;
while (current != null) {
if (node.Value > current.Value) {
predecessor = current;
current = current.Right;
} else {
current = current.Left;
}
}
return predecessor;
}Solution
Step 1: Analyze left subtree traversal
Predecessor in left subtree is rightmost node, so loop should move right, not left.Step 2: Identify incorrect loop condition
Current code loops while temp.Left != null, should be temp.Right != null to find rightmost node.Final Answer:
The while loop should check temp.Right, not temp.Left -> Option AQuick Check:
Rightmost node in left subtree = predecessor [OK]
- Checking left child repeatedly instead of right
- Ignoring null root case (not critical here)
- Confusing predecessor update logic
40
/ \
20 60
/ \ / \
10 30 50 70
/
25
Which node is the inorder predecessor of 25?Solution
Step 1: Check if node 25 has left subtree
Node 25 has no left child, so predecessor is ancestor smaller than 25.Step 2: Find nearest ancestor smaller than 25
Ancestor 20 is smaller than 25 and closest on path from root to 25.Final Answer:
20 -> Option CQuick Check:
Predecessor of 25 is 20 [OK]
- Choosing 30 which is larger than 25
- Choosing 10 which is smaller but not nearest ancestor
- Thinking 25 has no predecessor
Ready to prove it?
Pass the test and this pattern moves unknown
