173. Binary Search Tree Iterator
Problem Description
You need to implement a BSTIterator
class that allows you to iterate through a Binary Search Tree (BST) in in-order traversal sequence (left → root → right).
The class should have three main components:
-
Constructor
BSTIterator(TreeNode root)
: Takes the root of a BST and initializes the iterator. The internal pointer should start at a position before the smallest element (conceptually at a "non-existent" position smaller than any element in the tree). -
Method
next()
: Returns the next smallest number in the BST and advances the pointer. Since the pointer starts before the first element, the first call tonext()
will return the smallest element in the BST. -
Method
hasNext()
: Returnstrue
if there are more elements to iterate through (elements to the right of the current pointer position), andfalse
otherwise.
The key requirements are:
- The iteration should follow in-order traversal order, which for a BST means visiting nodes in ascending order of their values
- You can assume that
next()
will only be called when there are remaining elements (whenhasNext()
returnstrue
) - The iterator should efficiently traverse through all nodes exactly once
Example usage pattern:
obj = BSTIterator(root)
while obj.hasNext():
value = obj.next() # Gets next value in ascending order
The solution shown uses a preprocessing approach where it performs a complete in-order traversal during initialization to store all values in a list self.vals
. It then uses a cursor self.cur
to track the current position in this list. The next()
method returns the value at the current cursor position and increments it, while hasNext()
checks if the cursor has reached the end of the list.
Intuition
When we need to iterate through a BST in ascending order, we're essentially asking for an in-order traversal. The natural question is: how can we provide elements one at a time on demand, rather than all at once?
The most straightforward approach is to perform the complete in-order traversal upfront and store all values in a list. This way, we transform the tree traversal problem into a simple array iteration problem. Think of it like reading a book - instead of searching through scattered pages each time, we arrange all pages in order first, then simply flip through them one by one.
Why does this work well? In-order traversal of a BST naturally gives us elements in sorted (ascending) order. By collecting all values during initialization:
- We do the complex tree navigation work once
- The
next()
operation becomes a simple array access withO(1)
time - The
hasNext()
check is just comparing indices withO(1)
time
The trade-off here is space - we're using O(n)
extra space to store all node values. However, this makes the iterator operations extremely simple and efficient. We maintain a cursor (self.cur
) that starts at 0 and moves forward with each next()
call, mimicking how we'd iterate through a regular list.
This approach is like preparing a playlist before a party - you spend time upfront organizing the songs in order, but then playing the next song is instant. The alternative would be searching through your music library each time you need the next song, which would be more complex and potentially slower for each operation.
Solution Approach
The solution uses a preprocessing strategy with in-order traversal to collect all BST values during initialization. Here's the step-by-step implementation:
1. Initialization Phase (__init__
method)
The constructor sets up three key components:
self.cur = 0
: A cursor to track our current position in the traversalself.vals = []
: A list to store all BST values in sorted order- A nested
inorder
function that performs recursive in-order traversal
The inorder
function follows the classic recursive pattern:
def inorder(root):
if root:
inorder(root.left) # Visit left subtree
self.vals.append(root.val) # Process current node
inorder(root.right) # Visit right subtree
This traversal visits nodes in the exact order we need: left → root → right, which for a BST means ascending order. After initialization, self.vals
contains all values sorted from smallest to largest.
2. Next Element Retrieval (next
method)
This method is straightforward:
def next(self) -> int:
res = self.vals[self.cur] # Get value at current position
self.cur += 1 # Move cursor forward
return res
Since we've already arranged values in order, getting the next element is just array indexing at position self.cur
, then incrementing the cursor for the next call.
3. Checking for More Elements (hasNext
method)
The check is a simple boundary condition:
def hasNext(self) -> bool:
return self.cur < len(self.vals)
If the cursor hasn't reached the end of our values list, there are more elements to iterate through.
Time and Space Complexity
- Initialization:
O(n)
time to traverse alln
nodes,O(n)
space for the recursion stack and storing values - next():
O(1)
time - simple array access - hasNext():
O(1)
time - simple comparison
The total space complexity is O(n)
for storing all node values, which trades memory for the simplicity and speed of iterator operations.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with a small BST example:
4 / \ 2 6 / \ \ 1 3 7
Step 1: Initialization
When we create BSTIterator(root)
, the constructor:
- Sets
self.cur = 0
(cursor at start position) - Creates empty list
self.vals = []
- Performs in-order traversal:
- Visit node 1:
vals = [1]
- Visit node 2:
vals = [1, 2]
- Visit node 3:
vals = [1, 2, 3]
- Visit node 4:
vals = [1, 2, 3, 4]
- Visit node 6:
vals = [1, 2, 3, 4, 6]
- Visit node 7:
vals = [1, 2, 3, 4, 6, 7]
- Visit node 1:
After initialization:
self.vals = [1, 2, 3, 4, 6, 7]
(all values in ascending order)self.cur = 0
(pointing to index 0)
Step 2: First hasNext()
call
- Check:
cur (0) < len(vals) (6)
→ Returnstrue
Step 3: First next()
call
- Get
vals[0] = 1
- Increment cursor:
cur = 1
- Return
1
Step 4: Second next()
call
- Get
vals[1] = 2
- Increment cursor:
cur = 2
- Return
2
Step 5: Continue iterating...
next()
returns3
, cursor becomes3
next()
returns4
, cursor becomes4
next()
returns6
, cursor becomes5
next()
returns7
, cursor becomes6
Step 6: Final hasNext()
call
- Check:
cur (6) < len(vals) (6)
→ Returnsfalse
- No more elements to iterate!
The iterator successfully returned all BST values in ascending order: 1, 2, 3, 4, 6, 7
.
Solution Implementation
1# Definition for a binary tree node.
2# class TreeNode:
3# def __init__(self, val=0, left=None, right=None):
4# self.val = val
5# self.left = left
6# self.right = right
7
8class BSTIterator:
9 """
10 Iterator for Binary Search Tree that returns values in ascending order.
11 Uses in-order traversal to flatten the BST into a list during initialization.
12 """
13
14 def __init__(self, root: TreeNode) -> None:
15 """
16 Initialize the iterator with the root of a BST.
17 Performs in-order traversal to store all values in sorted order.
18
19 Args:
20 root: The root node of the binary search tree
21 """
22 # Initialize current index pointer and values list
23 self.current_index = 0
24 self.values = []
25
26 # Perform in-order traversal to populate values list
27 self._inorder_traversal(root)
28
29 def _inorder_traversal(self, node: TreeNode) -> None:
30 """
31 Helper method to perform in-order traversal of the BST.
32 Populates the values list with node values in ascending order.
33
34 Args:
35 node: Current node being processed
36 """
37 if node:
38 # Traverse left subtree
39 self._inorder_traversal(node.left)
40 # Process current node
41 self.values.append(node.val)
42 # Traverse right subtree
43 self._inorder_traversal(node.right)
44
45 def next(self) -> int:
46 """
47 Returns the next smallest element in the BST.
48
49 Returns:
50 The next smallest value in the BST
51 """
52 # Get the value at current index
53 result = self.values[self.current_index]
54 # Move pointer to next element
55 self.current_index += 1
56 return result
57
58 def hasNext(self) -> bool:
59 """
60 Checks if there are more elements to iterate.
61
62 Returns:
63 True if there are more elements, False otherwise
64 """
65 return self.current_index < len(self.values)
66
67
68# Your BSTIterator object will be instantiated and called as such:
69# obj = BSTIterator(root)
70# param_1 = obj.next()
71# param_2 = obj.hasNext()
72
1/**
2 * Definition for a binary tree node.
3 * public class TreeNode {
4 * int val;
5 * TreeNode left;
6 * TreeNode right;
7 * TreeNode() {}
8 * TreeNode(int val) { this.val = val; }
9 * TreeNode(int val, TreeNode left, TreeNode right) {
10 * this.val = val;
11 * this.left = left;
12 * this.right = right;
13 * }
14 * }
15 */
16
17/**
18 * Binary Search Tree Iterator implementation.
19 * Performs in-order traversal of BST and stores values in a list.
20 * Provides iterator functionality to traverse through BST values in ascending order.
21 */
22class BSTIterator {
23 // Current index pointer for iteration through the values list
24 private int currentIndex;
25
26 // List to store all BST values in sorted order (in-order traversal result)
27 private List<Integer> values;
28
29 /**
30 * Constructor initializes the iterator with the root of BST.
31 * Performs complete in-order traversal and stores all values.
32 * Time Complexity: O(n) where n is the number of nodes
33 * Space Complexity: O(n) for storing all node values
34 *
35 * @param root The root node of the binary search tree
36 */
37 public BSTIterator(TreeNode root) {
38 this.currentIndex = 0;
39 this.values = new ArrayList<>();
40 performInorderTraversal(root);
41 }
42
43 /**
44 * Returns the next smallest element in the BST.
45 * Time Complexity: O(1)
46 *
47 * @return The next smallest integer value in the BST
48 */
49 public int next() {
50 // Return current value and increment the index for next call
51 return values.get(currentIndex++);
52 }
53
54 /**
55 * Checks if there are more elements to iterate.
56 * Time Complexity: O(1)
57 *
58 * @return true if there are more elements, false otherwise
59 */
60 public boolean hasNext() {
61 // Check if current index is within the bounds of values list
62 return currentIndex < values.size();
63 }
64
65 /**
66 * Helper method to perform in-order traversal of the BST.
67 * Recursively traverses left subtree, processes current node, then right subtree.
68 * This ensures values are added to the list in ascending order.
69 *
70 * @param node The current node being processed in the traversal
71 */
72 private void performInorderTraversal(TreeNode node) {
73 // Base case: if node is null, return
74 if (node == null) {
75 return;
76 }
77
78 // Traverse left subtree first (smaller values)
79 performInorderTraversal(node.left);
80
81 // Process current node - add its value to the list
82 values.add(node.val);
83
84 // Traverse right subtree (larger values)
85 performInorderTraversal(node.right);
86 }
87}
88
89/**
90 * Your BSTIterator object will be instantiated and called as such:
91 * BSTIterator obj = new BSTIterator(root);
92 * int param_1 = obj.next();
93 * boolean param_2 = obj.hasNext();
94 */
95
1/**
2 * Definition for a binary tree node.
3 * struct TreeNode {
4 * int val;
5 * TreeNode *left;
6 * TreeNode *right;
7 * TreeNode() : val(0), left(nullptr), right(nullptr) {}
8 * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
9 * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
10 * };
11 */
12class BSTIterator {
13private:
14 std::vector<int> values; // Stores all BST values in sorted order
15 int currentIndex; // Current position in the values vector
16
17public:
18 /**
19 * Constructor: Initialize the iterator with the root of BST
20 * Performs inorder traversal to populate values in sorted order
21 * @param root: The root node of the binary search tree
22 */
23 BSTIterator(TreeNode* root) {
24 currentIndex = 0;
25 performInorderTraversal(root);
26 }
27
28 /**
29 * Returns the next smallest number in the BST
30 * @return: The value at current position and advances the iterator
31 */
32 int next() {
33 return values[currentIndex++];
34 }
35
36 /**
37 * Checks if there are more elements to iterate
38 * @return: true if there are more elements, false otherwise
39 */
40 bool hasNext() {
41 return currentIndex < values.size();
42 }
43
44private:
45 /**
46 * Helper function to perform inorder traversal of BST
47 * Populates the values vector with node values in sorted order
48 * @param node: Current node being processed
49 */
50 void performInorderTraversal(TreeNode* node) {
51 if (node != nullptr) {
52 // Traverse left subtree
53 performInorderTraversal(node->left);
54
55 // Process current node
56 values.push_back(node->val);
57
58 // Traverse right subtree
59 performInorderTraversal(node->right);
60 }
61 }
62};
63
64/**
65 * Your BSTIterator object will be instantiated and called as such:
66 * BSTIterator* obj = new BSTIterator(root);
67 * int param_1 = obj->next();
68 * bool param_2 = obj->hasNext();
69 */
70
1/**
2 * Definition for a binary tree node.
3 * class TreeNode {
4 * val: number
5 * left: TreeNode | null
6 * right: TreeNode | null
7 * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
8 * this.val = (val===undefined ? 0 : val)
9 * this.left = (left===undefined ? null : left)
10 * this.right = (right===undefined ? null : right)
11 * }
12 * }
13 */
14
15// Array to store the in-order traversal values
16let inorderValues: number[] = [];
17// Current position in the iterator
18let currentIndex: number = 0;
19
20/**
21 * Initializes the BST iterator with the root of the BST
22 * Performs in-order traversal to flatten the tree into an array
23 * @param root - The root node of the binary search tree
24 */
25function BSTIterator(root: TreeNode | null): void {
26 // Reset the state for new iterator instance
27 currentIndex = 0;
28 inorderValues = [];
29
30 /**
31 * Helper function to perform in-order traversal (left -> root -> right)
32 * This ensures values are stored in ascending order for BST
33 * @param node - Current node being processed
34 */
35 const performInorderTraversal = (node: TreeNode | null): void => {
36 // Base case: if node is null, return
37 if (node === null) {
38 return;
39 }
40
41 // Traverse left subtree first
42 performInorderTraversal(node.left);
43 // Process current node by adding its value to the array
44 inorderValues.push(node.val);
45 // Traverse right subtree last
46 performInorderTraversal(node.right);
47 };
48
49 // Start the in-order traversal from root
50 performInorderTraversal(root);
51}
52
53/**
54 * Returns the next smallest element in the BST
55 * @returns The next smallest value in the iterator
56 */
57function next(): number {
58 // Return the value at current index and increment the index
59 return inorderValues[currentIndex++];
60}
61
62/**
63 * Checks if there are more elements to iterate
64 * @returns True if there are more elements, false otherwise
65 */
66function hasNext(): boolean {
67 // Check if current index is within bounds of the array
68 return currentIndex < inorderValues.length;
69}
70
71/**
72 * Your BSTIterator object will be instantiated and called as such:
73 * BSTIterator(root)
74 * var param_1 = next()
75 * var param_2 = hasNext()
76 */
77
Time and Space Complexity
Time Complexity:
__init__
:O(n)
wheren
is the number of nodes in the BST. The inorder traversal visits each node exactly once to build the values array.next()
:O(1)
as it simply accesses an element from the array by index and increments the cursor.hasNext()
:O(1)
as it only performs a simple comparison between the cursor and array length.
Space Complexity:
- Overall:
O(n)
wheren
is the number of nodes in the BST.- The
self.vals
array stores alln
values from the tree. - The recursive inorder traversal uses
O(h)
space for the call stack, whereh
is the height of the tree. In the worst case (skewed tree),h = n
, and in the best case (balanced tree),h = log(n)
. - Since we're storing all values in the array, the dominant space complexity is
O(n)
.
- The
Note: This implementation trades space for simplicity by pre-computing all values during initialization. An alternative approach using a stack could achieve O(h)
average space complexity by computing values on-demand during iteration.
Common Pitfalls
1. Memory Inefficiency for Large Trees
The preprocessing approach stores all n
values upfront, which can be problematic for very large BSTs where memory is constrained. If the BST contains millions of nodes but you only need to iterate through a few elements, you're wasting significant memory.
Solution: Use a lazy evaluation approach with a stack to store only the path to the current node:
class BSTIterator:
def __init__(self, root: TreeNode):
self.stack = []
self._push_all_left(root)
def _push_all_left(self, node):
while node:
self.stack.append(node)
node = node.left
def next(self) -> int:
node = self.stack.pop()
if node.right:
self._push_all_left(node.right)
return node.val
def hasNext(self) -> bool:
return len(self.stack) > 0
This approach uses O(h)
space where h
is the height of the tree, instead of O(n)
.
2. Handling Empty Trees
The current solution works correctly for empty trees (when root
is None
), but developers might forget to test this edge case or might add unnecessary null checks.
What to verify:
- Empty tree should result in
hasNext()
returningFalse
immediately - The values list remains empty, so the cursor logic still works
3. Thread Safety Issues
If multiple threads try to use the same iterator instance concurrently, the shared current_index
variable can lead to race conditions where:
- Two threads might get the same value from
next()
- The index might skip values or go out of bounds
Solution: Either:
- Document that the iterator is not thread-safe
- Add synchronization locks around state modifications
- Create separate iterator instances for each thread
4. Incorrect Assumption About BST Properties
Developers might assume the BST is balanced or has specific properties. The solution works for any valid BST structure (balanced or unbalanced), but performance characteristics differ:
- Balanced BST: Stack approach uses
O(log n)
space - Skewed BST: Stack approach uses
O(n)
space in worst case
Always consider worst-case scenarios when analyzing complexity.
Which algorithm is best for finding the shortest distance between two points in an unweighted graph?
Recommended Readings
Stack Intro Imagine you have a pile of books on your desk If you want to add a new book you place it on top If you want to read a book you take it from the top And if you simply want to see which book is on the top you
Everything About Trees A tree is a type of graph data structure composed of nodes and edges Its main properties are It is acyclic doesn't contain any cycles There exists a path from the root to any node Has N 1 edges where N is the number of nodes in the tree and
Binary Search Tree Intro Definition A Binary Search Tree or BST is a rooted binary tree with the value of each internal node being greater than all the values in the respective node's left subtree and less than the ones in its right subtree An empty tree is a BST since it satisfies the
Want a Structured Path to Master System Design Too? Don’t Miss This!