Description

Binary Search Tree Iterator

Implement the BSTIterator class that represents an iterator over the in-order traversal of a binary search tree (BST):

  • BSTIterator(TreeNode root) Initializes an object of the BSTIterator class. The root of the BST is given as part of the constructor. The pointer should be initialized to a non-existent number smaller than any element in the BST.
  • boolean hasNext() Returns true if there exists a number in the traversal to the right of the pointer, otherwise returns false.
  • int next() Moves the pointer to the right, then returns the number at the pointer.

Notice that by initializing the pointer to a non-existent smallest number, the first call to next() will return the smallest element in the BST.

You may assume that next() calls will always be valid. That is, there will be at least a next number in the in-order traversal when next() is called.

Example 1:

Input
["BSTIterator", "next", "next", "hasNext", "next", "hasNext", "next", "hasNext", "next", "hasNext"]
[[[7, 3, 15, null, null, 9, 20]], [], [], [], [], [], [], [], [], []]
Output
[null, 3, 7, true, 9, true, 15, true, 20, false]

Explanation
BSTIterator bSTIterator = new BSTIterator([7, 3, 15, null, null, 9, 20]);
bSTIterator.next(); // return 3
bSTIterator.next(); // return 7
bSTIterator.hasNext(); // return True
bSTIterator.next(); // return 9
bSTIterator.hasNext(); // return True
bSTIterator.next(); // return 15
bSTIterator.hasNext(); // return True
bSTIterator.next(); // return 20
bSTIterator.hasNext(); // return False

Constraints:

  • The number of nodes in the tree is in the range [1, 105].
  • 0 <= Node.val <= 106
  • At most 105 calls will be made to hasNext, and next.

Follow up:

  • Could you implement next() and hasNext() to run in average O(1) time and use O(h) memory, where h is the height of the tree?

Primary Approach: Controlled Inorder Stack ( Amortized Time, Space)

Intuition

To iterate through a BST in sorted ascending order () without storing all elements upfront, simulate recursion iteratively using an explicit Stack:

  1. Constructor: Push the root and all of its left descendants onto the stack until reaching null. The top of the stack now holds the smallest unvisited element.
  2. next(): Pop the top node from the stack. Before returning its value, if this node has a right child, push that right child and all of its left descendants onto the stack.
  3. hasNext(): Simply return whether the stack is non-empty (!stack.isEmpty()).
import java.util.ArrayDeque;
import java.util.Deque;
 
class BSTIterator {
    private Deque<TreeNode> stack = new ArrayDeque<>();
 
    public BSTIterator(TreeNode root) {
        // Initialize stack with leftmost path
        pushAllLeft(root);
    }
    
    public int next() {
        TreeNode node = stack.pop();
        // If popped node has a right child, process its left branch
        if (node.right != null) {
            pushAllLeft(node.right);
        }
        return node.val;
    }
    
    public boolean hasNext() {
        return !stack.isEmpty();
    }
 
    private void pushAllLeft(TreeNode node) {
        while (node != null) {
            stack.push(node);
            node = node.left;
        }
    }
}
 

Complexity

  • Time Complexity:
    • next(): Amortized — While a single next() call can take time to push left nodes, each node in the tree is pushed and popped from the stack at most once over total calls ( calls total work).
    • hasNext(): worst-case.
  • Space Complexity: — The stack stores at most nodes at any point in time, where is the tree height ( for balanced trees, for skewed trees).

Alternative Trade-off: Pre-computed Inorder List ( Time, Space)

Intuition

Perform a complete inorder DFS traversal in the constructor to flatten the BST into an ArrayList, then track current position with an index pointer:

  • Why Interviewers Reject This as Primary: While next() and hasNext() are strictly , it violates the problem’s strict memory constraint by storing all nodes in memory upfront.
import java.util.ArrayList;
import java.util.List;
 
class BSTIterator {
    private List<Integer> inorderList = new ArrayList<>();
    private int index = 0;
 
    public BSTIterator(TreeNode root) {
        inorder(root);
    }
 
    private void inorder(TreeNode node) {
        if (node == null) return;
        inorder(node.left);
        inorderList.add(node.val);
        inorder(node.right);
    }
    
    public int next() {
        return inorderList.get(index++);
    }
    
    public boolean hasNext() {
        return index < inorderList.size();
    }
}
 

Complexity

  • Time Complexity: for both next() and hasNext(). Constructor takes .
  • Space Complexity: — Stores all tree elements in a list.

Key Interview Discussion Points

  • Amortized Analysis Explanation: When asked why next() is amortized despite the while loop, explain: “Across calls to next(), every node in the BST is pushed onto the stack exactly once and popped exactly once. Therefore, total time for calls is , giving an amortized cost of per operation.”
  • Memory Efficiency: Highlight that using the Controlled Stack keeps space bounded by tree height , which is vastly superior to pre-fetching all nodes when handling massive datasets or streams.

Easy Memory Rule

“Constructor Push left line down next() Pop top, then push right child’s left line hasNext() !stack.isEmpty()!”