BST Iterator Design in DSA Java - Time & Space Complexity
We want to understand how fast the BST Iterator works when we ask for the next smallest element.
How does the time to get the next element change as the tree grows bigger?
Analyze the time complexity of the following BST Iterator methods.
public class BSTIterator {
private Stack stack = new Stack<>();
public BSTIterator(TreeNode root) {
pushLeft(root);
}
private void pushLeft(TreeNode node) {
while (node != null) {
stack.push(node);
node = node.left;
}
}
public int next() {
TreeNode node = stack.pop();
pushLeft(node.right);
return node.val;
}
public boolean hasNext() {
return !stack.isEmpty();
}
}
This code creates an iterator over a binary search tree that returns elements in ascending order.
Look at what repeats when we call next().
- Primary operation: Popping from the stack and pushing left children of the right subtree.
- How many times: Each node is pushed and popped exactly once during the entire iteration.
Getting the next element sometimes takes a few steps to push nodes, sometimes just one pop.
| Input Size (n) | Approx. Operations per next() |
|---|---|
| 10 | Usually 1 to 2 steps |
| 100 | Usually 1 to 3 steps |
| 1000 | Usually 1 to 4 steps |
Pattern observation: Each node is handled once, so average work per next() call stays small even as tree grows.
Time Complexity: O(1) amortized
This means each call to next() takes constant time on average, even if some calls take longer.
[X] Wrong: "Each next() call always takes time proportional to the height of the tree."
[OK] Correct: Because the pushing of nodes is spread out over all calls, the average time per call is constant, not always the height.
Understanding this iterator's time complexity shows you can analyze how work spreads over many operations, a key skill in coding interviews.
What if we changed the iterator to push all nodes into a list at the start? How would the time complexity of next() change?
Practice
BST Iterator in the context of a Binary Search Tree?Solution
Step 1: Understand BST Iterator functionality
A BST Iterator is designed to traverse the tree in sorted order, typically ascending, without using recursion explicitly.Step 2: Identify the main purpose
It provides a way to get the next smallest element efficiently, which matches To traverse the BST in ascending order without recursion.Final Answer:
To traverse the BST in ascending order without recursion -> Option AQuick Check:
BST Iterator = Ascending traversal [OK]
- Confusing iterator with insertion or deletion operations
- Thinking it balances the tree automatically
- Assuming it uses recursion internally
next() method in a BST Iterator class in Java?Solution
Step 1: Understand the return type of next()
Thenext()method returns the next smallest integer value from the BST, so it should return an int.Step 2: Match method signature
public int next() matches the expected signature:public int next().Final Answer:
public int next() -> Option CQuick Check:
next() returns int value [OK]
- Using void return type for next()
- Returning boolean instead of int
- Returning Node instead of int value
next() calls?
BSTIterator iterator = new BSTIterator(root); // BST with nodes 3, 7, 9 System.out.print(iterator.next() + ", "); System.out.print(iterator.next() + ", "); System.out.print(iterator.next());
Solution
Step 1: Understand BST in-order traversal order
BST in-order traversal returns nodes in ascending order: 3, 7, 9.Step 2: Trace next() calls
Each next() call returns the next smallest element, so the output is 3, then 7, then 9.Final Answer:
3, 7, 9 -> Option BQuick Check:
In-order traversal = 3,7,9 [OK]
- Assuming next() returns descending order
- Mixing up node values order
- Confusing next() with hasNext()
next() method implementation:
public int next() {
TreeNode node = stack.pop();
if (node.right != null) {
pushLeft(node.right);
}
return node.val;
}
private void pushLeft(TreeNode node) {
while (node != null) {
stack.push(node);
node = node.left;
}
}Solution
Step 1: Analyze next() method stack usage
Calling stack.pop() without checking if stack is empty can cause runtime error if no elements remain.Step 2: Identify missing safety check
Proper implementation should check stack emptiness before popping to avoid exceptions.Final Answer:
The next() method should check if stack is empty before popping -> Option DQuick Check:
Check stack empty before pop [OK]
- Ignoring stack empty check causing exceptions
- Misunderstanding pushLeft logic
- Returning wrong node value
peek() to see the next smallest element without advancing the iterator. Which approach correctly extends the standard BST Iterator design?Solution
Step 1: Understand peek() requirement
peek() should return next smallest element without advancing iterator, so it must not pop stack.Step 2: Identify correct approach
Storing next smallest element in a variable during next() allows peek() to return it without changing stack state, matching Store the next smallest element in a variable duringnext()and return it inpeek()without popping stack.Final Answer:
Store the next smallest element in a variable during next() and return it in peek() without popping stack -> Option AQuick Check:
peek() returns next without advancing [OK]
- Popping stack in peek() advancing iterator
- Using extra stack unnecessarily
- Precomputing all values losing iterator benefits
Ready to prove it?
Pass the test and this pattern moves unknown
