Binary Search Tree Iterator

Updated on 12 May, 2025
Binary Search Tree Iterator header image

Problem Statement

The task is to implement a BSTIterator class that allows iterating over a binary search tree (BST) using its in-order traversal. The key operations provided by this iterator are:

  • BSTIterator(TreeNode root) - This constructor initializes a new iterator object, receiving the root of the BST. A pointer is used to manage the traversal and is initially set before the smallest element.
  • boolean hasNext() - Checks and returns true if there are more elements to iterate over in the BST to the right of the current position of the pointer.
  • int next() - Moves the pointer to the right and returns the current element at the pointer after the move.

The iterator cleverly uses the natural ordering of elements in a BST to systematically access each element in ascending order. When the next() method is initially called, it yields the smallest element in the BST, ensuring intuitive and predictable behavior of the iterator.

Examples

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:

  1. BSTIterator bSTIterator = new BSTIterator([7, 3, 15, null, null, 9, 20]);
    Constructs the iterator for the given BST.

  2. bSTIterator.next(); ➜ returns 3

  3. bSTIterator.next(); ➜ returns 7

  4. bSTIterator.hasNext(); ➜ returns true

  5. bSTIterator.next(); ➜ returns 9

  6. bSTIterator.hasNext(); ➜ returns true

  7. bSTIterator.next(); ➜ returns 15

  8. bSTIterator.hasNext(); ➜ returns true

  9. bSTIterator.next(); ➜ returns 20

  10. bSTIterator.hasNext(); ➜ returns 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.

Approach and Intuition

The BSTIterator leverages the properties of in-order traversal, which processes elements of a BST in non-decreasing order. This is achieved by using the following approach:

  1. Initialization with Stack:

    • During the initialization (BSTIterator), a stack is prepared to simulate the recursive nature of in-order traversal iteratively. This stack manages the current nodes to be processed.
    • Begin from the given root and push all left children onto the stack, reaching the minimum element first.
  2. Element Retrieval (next method):

    • To retrieve the next element in order, the top node of the stack is popped. This node corresponds to the next smallest element.
    • If the popped node has a right subtree, proceed to push all of its left children onto the stack (similar to the initialization process). This step ensures that the next smallest element is correctly lined up for subsequent calls.
  3. Checking Continuation (hasNext method):

    • This simply checks whether there are any elements left in the stack. A non-empty stack indicates that there are further elements to process in the traversal, hence hasNext returns true.
    • If the stack is empty, it returns false indicating that the traversal is complete.

Through this approach, the class efficiently manages the traversal's state without needing to store the entire traversal results upfront. This makes it particularly memory-efficient for large trees. Each call to next() works in amortized O(1) time, maintaining the stack with the next elements to be processed.

Given the constraints, this method is well-suited as it processes nodes on-demand and handles up to 105 calls efficiently. The example given clearly illustrates the functioning of the iterator under typical scenarios. The sequence of operations and their results match the expected outcome based on in-order traversal logic.

Solutions

  • C++
  • Java
  • Python
cpp
class BinarySearchTreeIterator {
public:
    stack<TreeNode*> nodeStack;

    BinarySearchTreeIterator(TreeNode* root) {
        this->pushAllLeft(root);
    }

    void pushAllLeft(TreeNode* node) {
        while (node != NULL) {
            this->nodeStack.push(node);
            node = node->left;
        }
    }

    int next() {
        TreeNode* smallestNode = this->nodeStack.top();
        this->nodeStack.pop();

        if (smallestNode->right != NULL) {
            this->pushAllLeft(smallestNode->right);
        }

        return smallestNode->val;
    }

    bool hasNext() { return !this->nodeStack.empty(); }
};

This C++ solution implements an iterator for a Binary Search Tree (BST) using a stack to manage the traversal. The primary goal is to provide a way to access the tree's nodes in ascending order without needing to store all of the nodes at once. This method efficiently handles memory and executes operations.

  • Key Components:

    • A stack<TreeNode*> is used where each node's leftmost children are pushed onto the stack. This approach simulates an in-order traversal through the tree.
    • The constructor BinarySearchTreeIterator(TreeNode* root) initializes the stack by pushing all left children of the root.
    • The pushAllLeft(TreeNode* node) function assists by pushing all left child nodes onto the stack, starting from the specified node until there are no more left children.
    • The next() method:
      • Retrieves and removes the top item from the stack.
      • If the node has a right subtree, it pushes all of its left children onto the stack.
      • Returns the value of the node.
    • The hasNext() function simply checks if the stack is empty, indicating whether there are more nodes to visit.
  • Usage:

    • Create an instance of BinarySearchTreeIterator using the root of the BST.
    • Use hasNext() to check if the next smallest element is available before calling next() to retrieve it.

This approach effectively mimics threaded in-order traversal, ensuring that each call to next() returns the next smallest element in the BST. The use of a stack facilitates a non-recursive approach to in-order traversal, making the space complexity directly proportional to the tree's height, not its size.

java
class BSTTraversal {
    Stack<TreeNode> nodeStack;

    public BSTTraversal(TreeNode root) {
        nodeStack = new Stack<TreeNode>();
        pushAllLeftChildren(root);
    }

    private void pushAllLeftChildren(TreeNode root) {
        while (root != null) {
            nodeStack.push(root);
            root = root.left;
        }
    }

    public int next() {
        TreeNode currentNode = nodeStack.pop();

        if (currentNode.right != null) {
            pushAllLeftChildren(currentNode.right);
        }

        return currentNode.val;
    }

    public boolean hasNext() {
        return !nodeStack.isEmpty();
    }
}

This Java solution implements an iterator for a Binary Search Tree (BST) using a stack data structure to ensure that the next element returned is always the next smallest element, adhering strictly to the in-order traversal sequence. The structure, named BSTTraversal, efficiently handles the iterative traversal of the BST.

Initialization:

  • Initialize a stack to store nodes.
  • Start from the root and push all left children of the nodes onto the stack until a node with no left child is encountered. This setup is performed in pushAllLeftChildren method, which is called in the constructor of BSTTraversal.

Functionality:

  • next():

    • This method pops the top node from the stack, which corresponds to the next smallest element in the BST due to the properties of in-order traversal.
    • If the popped node has a right child, then the method pushAllLeftChildren is called with this right child as the argument to ensure that all left children of this subtree are included in the stack for future calls to next.
    • The value of the popped node is returned.
  • hasNext():

    • This simple method checks if the stack is empty. If it is not, it implies that there are still nodes to be processed, and thus it returns true. Otherwise, it returns false.

Usage: This structure allows for on-demand traversal of the BST, where memory overhead is reduced to the height of the tree, making it suitable for scenarios where the tree is modified concurrently or only partial traversal is necessary. Such on-demand traversal avoids the need to store all elements of the BST at once, leading to more efficient usage of resources.

To sum up, the BSTTraversal class offers an efficient way to iteratively traverse a BST in ascending order using an explicit stack to manage the traversal state, this approach leverages the inherent order of BSTs to provide quick access to the next smallest element without having to reprocess entire subtrees unnecessarily.

python
# Definition for a binary tree node.
class TreeNode:
    def __init__(self, x: int):
        self.val = x
        self.left = None
        self.right = None

class BinaryTreeIterator:

    def __init__(self, root: TreeNode) -> None:
        self.nodeStack = []
        self._pushLeftBranch(root)

    def _pushLeftBranch(self, node: TreeNode) -> None:
        while node:
            self.nodeStack.append(node)
            node = node.left

    def next(self) -> int:
        smallest_node = self.nodeStack.pop()
        if smallest_node.right:
            self._pushLeftBranch(smallest_node.right)
        return smallest_node.val

    def hasNext(self) -> bool:
        return len(self.nodeStack) > 0

Implement an iterator for a Binary Search Tree (BST) using Python that allows you to traverse the tree's nodes in ascending order. This solution details how to control the traversal using a custom iterator class BinaryTreeIterator. The implementation involves setting up the tree structure with a TreeNode class, and an iterator mechanism that uses a stack (nodeStack) to hold nodes.

  • Initialize BinaryTreeIterator with the root node. Populate nodeStack with all left children, starting from the root. This ensures that the smallest element is at the top of the stack.

  • The next() function pops the top element from nodeStack, which represents the next smallest number in the BST. After returning the smallest number, push all left children of the popped node's right child onto the stack.

  • The hasNext() method simply checks if any elements are left in nodeStack, which determines whether further traversal is possible.

The implementation ensures that each call to next() is efficient and that the space complexity is O(h), where h is the height of the tree. This efficiency is crucial for performance-critical applications.

By the completion of this setup, you have a robust iterator for a BST that efficiently traverses nodes in sorted order, allowing both BST element access in amortized O(1) time and initialization in O(N) time where N is the number of nodes. The clarity and efficiency of this approach make it highly suitable for use in data processing and manipulation tasks.

Comments

No comments yet.