BST Inorder Predecessor in DSA Java - Time & Space Complexity
We want to understand how long it takes to find the inorder predecessor in a Binary Search Tree (BST).
Specifically, how the time grows as the tree gets bigger.
Analyze the time complexity of the following code snippet.
// Find inorder predecessor in BST
Node inorderPredecessor(Node root, Node target) {
if (target.left != null) {
Node curr = target.left;
while (curr.right != null) {
curr = curr.right;
}
return curr;
}
Node predecessor = null;
Node curr = root;
while (curr != null) {
if (target.data > curr.data) {
predecessor = curr;
curr = curr.right;
} else {
curr = curr.left;
}
}
return predecessor;
}
This code finds the inorder predecessor of a given node in a BST by checking left subtree or ancestors.
Look for loops or repeated steps in the code.
- Primary operation: Traversing down the tree nodes using while loops.
- How many times: At most, the number of nodes along a path from root to leaf or target's left subtree height.
As the BST grows, the longest path from root to leaf increases.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | Up to 4-5 steps (tree height) |
| 100 | Up to 7-8 steps |
| 1000 | Up to 10 steps |
Pattern observation: The steps grow roughly with the height of the tree, not 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 time proportional to the total number of nodes (n)."
[OK] Correct: The search only follows a path down the tree, not all nodes, so it depends on the tree height, not total nodes.
Understanding this helps you explain how tree shape affects search times, a key skill for working with BSTs and related data structures.
"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, so predecessor is the previous node in this order.Step 2: Define inorder predecessor
The inorder predecessor is the node with the largest value smaller than the current node's value.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: Identify correct parameters for BST predecessor method
We need the BST root and the key node value to find predecessor, so method should accept Node root and int key.Step 2: Check method return type and name
Return type should be Node (the predecessor node), and method name should indicate predecessor, so 'findInorderPredecessor(Node root, int key)' fits.Final Answer:
public Node findInorderPredecessor(Node root, int key) -> Option CQuick Check:
Correct signature includes root, key, returns Node [OK]
- Using array instead of BST Node
- Confusing predecessor with successor method name
- Wrong return type like void or int
20 / \ 10 30 Node predecessor = findInorderPredecessor(root, 20); System.out.println(predecessor != null ? predecessor.data : "null");Assuming
findInorderPredecessor is correctly implemented.Solution
Step 1: Identify inorder predecessor of node 20 in BST
Inorder predecessor is the largest node smaller than 20. Left subtree of 20 has node 10, which is smaller and largest in left subtree.Step 2: Confirm output of print statement
Predecessor node data is 10, so printed output is "10".Final Answer:
10 -> Option BQuick Check:
Largest smaller node than 20 is 10 [OK]
- Printing null when left subtree exists
- Confusing successor with predecessor
- Printing the node itself instead of predecessor
public Node findInorderPredecessor(Node root, int key) {
Node predecessor = null;
while (root != null) {
if (key < root.data) {
root = root.left;
} else {
predecessor = root;
root = root.right;
}
}
return predecessor;
}
What is the main issue with this code?Solution
Step 1: Analyze traversal logic for predecessor
The code moves left if key < root.data, else moves right and updates predecessor. But it never checks left subtree of the node itself.Step 2: Identify missing left subtree handling
If the node has a left subtree, the predecessor is the maximum node in that left subtree, which this code does not check.Final Answer:
It does not handle the case when the node has a left subtree -> Option AQuick Check:
Missing left subtree max check causes wrong predecessor [OK]
- Ignoring left subtree max node
- Confusing predecessor with successor logic
- Assuming predecessor is always ancestor
Solution
Step 1: Understand predecessor when no left child
If node has no left child, predecessor is one of its ancestors with value less than node's value.Step 2: Use traversal from root to track predecessor
Traverse from root to node 15, keep updating predecessor to last node with value less than 15.Final Answer:
Traverse from root, keep track of last node with value less than 15 -> Option DQuick Check:
No left child means ancestor with smaller value is predecessor [OK]
- Returning null immediately if no left child
- Trying to find max in non-existent left subtree
- Returning right child mistakenly
Ready to prove it?
Pass the test and this pattern moves unknown
