BST Iterator Design in DSA Typescript - Time & Space Complexity
We want to understand how fast the BST Iterator works when we move through a binary search tree.
Specifically, how the time to get the next smallest number changes as the tree grows.
Analyze the time complexity of this BST Iterator code snippet.
class BSTIterator {
private stack: TreeNode[] = [];
constructor(root: TreeNode | null) {
this.pushLeft(root);
}
private pushLeft(node: TreeNode | null) {
while (node) {
this.stack.push(node);
node = node.left;
}
}
next(): number {
const node = this.stack.pop()!;
this.pushLeft(node.right);
return node.val;
}
hasNext(): boolean {
return this.stack.length > 0;
}
}
This code creates an iterator that returns the next smallest value in a BST by using a stack to track nodes.
Look at what repeats when we call next():
- Primary operation: Pushing nodes along the left branch of a subtree onto the stack.
- How many times: Each node is pushed and popped exactly once during the entire iteration.
Imagine the tree has n nodes.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 pushes and 10 pops total over all next() calls. |
| 100 | About 100 pushes and 100 pops total over all next() calls. |
| 1000 | About 1000 pushes and 1000 pops total over all next() calls. |
Pattern observation: The total work grows linearly with the number of nodes, but each next() call does a small part of this work.
Time Complexity: O(1) average per next() call, O(h) worst case, where h is tree height.
This means each call to next() usually takes constant time, but sometimes it takes longer depending on the tree shape.
[X] Wrong: "Each next() call always takes O(h) time because it pushes nodes."
[OK] Correct: Actually, the total pushes and pops over all calls add up to O(n), so on average each call is O(1). The expensive steps are spread out.
Understanding this iterator's time complexity shows you can analyze how work spreads over many operations, a key skill for real coding problems.
What if we changed the iterator to use recursion instead of a stack? How would the time complexity change?
Practice
BST Iterator in 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 loading all nodes into memory at once.Step 2: Compare options with BST Iterator purpose
Options B, C, and D relate to modifying the tree, not iterating over it. To traverse the BST in ascending order without storing all elements at once correctly describes the iterator's purpose.Final Answer:
To traverse the BST in ascending order without storing all elements at once -> Option AQuick Check:
BST Iterator = In-order traversal on demand [OK]
- Confusing iterator with insertion or deletion operations
- Thinking iterator balances the tree
- Assuming iterator stores all nodes at once
next() method in a BST Iterator class that returns the next smallest number?Solution
Step 1: Understand the purpose of next()
Thenext()method should return the next smallest number in the BST, so it must return a number.Step 2: Analyze method signatures
next(): number returns a number with no parameters, which fits the iterator pattern. next(): void returns void, so no value is returned. next(value: number): number takes a parameter, which is not needed. next(): boolean returns boolean, which is incorrect.Final Answer:
next(): number -> Option CQuick Check:
next() returns next smallest number [OK]
- Adding unnecessary parameters to next()
- Returning void instead of number
- Returning boolean instead of number
const root = {
val: 7,
left: { val: 3, left: null, right: null },
right: { val: 15, left: { val: 9, left: null, right: null }, right: { val: 20, left: null, right: null } }
};
const iterator = new BSTIterator(root);
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.hasNext());
console.log(iterator.next());Solution
Step 1: Understand BST in-order traversal order
The BST nodes in ascending order are 3, 7, 9, 15, 20.Step 2: Trace iterator calls
First next() returns 3, second next() returns 7, hasNext() returns true because more nodes exist, third next() returns 9.Final Answer:
[3, 7, true, 9] -> Option AQuick Check:
In-order traversal = 3,7,9,... [OK]
- Mixing up left and right subtree order
- Assuming hasNext() returns next value
- Confusing node values order
class BSTIterator {
stack: TreeNode[] = [];
constructor(root: TreeNode | null) {
this.pushLeft(root);
}
pushLeft(node: TreeNode | null) {
while (node !== null) {
this.stack.push(node);
node = node.right; // Bug here
}
}
next(): number {
const node = this.stack.pop()!;
this.pushLeft(node.right);
return node.val;
}
hasNext(): boolean {
return this.stack.length > 0;
}
}Solution
Step 1: Analyze pushLeft method
pushLeft should push all left children to stack to simulate in-order traversal. The code incorrectly moves to node.right instead of node.left.Step 2: Check other methods
next() correctly pops from stack and pushes left children of right subtree. hasNext() correctly checks stack length. Constructor correctly calls pushLeft.Final Answer:
In pushLeft, should traverse left child, not right -> Option BQuick Check:
pushLeft must go left to simulate in-order [OK]
- Traversing right child in pushLeft instead of left
- Misunderstanding stack usage in iterator
- Incorrect pop direction in next()
peek() to see the next smallest element without advancing the iterator. Which approach best supports this feature efficiently?Solution
Step 1: Understand peek() requirement
peek() must return next smallest element without moving forward, so the next element must be accessible without popping.Step 2: Evaluate approaches
Use a stack to store left children and keep the top as peek value uses a stack with left children; the top of the stack is the next smallest element, so peek() can return it efficiently. Store all BST elements in an array and use an index pointer stores all elements but uses extra memory. Traverse the BST every time peek() is called is inefficient as it traverses repeatedly. Use a queue to store nodes in BFS order uses BFS order, which is not sorted.Final Answer:
Use a stack to store left children and keep the top as peek value -> Option DQuick Check:
Stack top = next smallest for peek() [OK]
- Using BFS queue which is unordered
- Re-traversing tree on every peek() call
- Storing all elements wastes memory
Ready to prove it?
Pass the test and this pattern moves unknown
