Binary Search Tree Iterator II

Updated on 20 May, 2025
Binary Search Tree Iterator II header image

Problem Statement

The BSTIterator class is designed to provide a controlled way of iterating through a binary search tree (BST) using its in-order traversal. The features of the class include:

  • Initialization of the iterator with the root of the BST.
  • Moving the internal pointer to simulate steps through the traversal, both forward and backwards.
  • Checking the availability of next and previous nodes relative to the current position of the pointer.

Key functionalities:

  1. Initialization (BSTIterator(TreeNode root)): Constructs an instance with the given tree's root. Internally, it might store the elements in a way that supports efficient in-order traversal from the smallest to largest element.
  2. Check Next (boolean hasNext()): Determines if there are nodes ahead in the traversal sequence, returning true or false.
  3. Next Element (int next()): Moves to the next element in the in-order sequence and returns its value.
  4. Check Previous (boolean hasPrev()): Checks whether elements are available before the current pointer in the traversal sequence.
  5. Previous Element (int prev()): Moves to the previous element in the sequence, returning its value.

The BSTIterator initiates its pointer at a virtual position before the smallest element, ensuring that the first next() invocation will correctly return the smallest element in the tree.

Examples

Example 1

Input:

["BSTIterator", "next", "next", "prev", "next", "hasNext", "next", "next", "next", "hasNext", "hasPrev", "prev", "prev"]
[[[7, 3, 15, null, null, 9, 20]], [], [], [], [], [], [], [], [], [], [], [], []]

Output:

[null, 3, 7, 3, 7, true, 9, 15, 20, false, true, 15, 9]

Explanation: Let the BST in-order sequence be [3, 7, 9, 15, 20].

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

  2. bSTIterator.next(); ➜ returns 3

  3. bSTIterator.next(); ➜ returns 7

  4. bSTIterator.prev(); ➜ returns 3

  5. bSTIterator.next(); ➜ returns 7

  6. bSTIterator.hasNext(); ➜ returns true

  7. bSTIterator.next(); ➜ returns 9

  8. bSTIterator.next(); ➜ returns 15

  9. bSTIterator.next(); ➜ returns 20

  10. bSTIterator.hasNext(); ➜ returns false

  11. bSTIterator.hasPrev(); ➜ returns true

  12. bSTIterator.prev(); ➜ returns 15

  13. bSTIterator.prev(); ➜ returns 9

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, next, hasPrev, and prev.

Approach and Intuition

The problem revolves around simulating an in-order traversal on a BST using an iterator with additional capabilities like checking for next and previous elements and moving the pointer accordingly.

Key Concepts in Play:

  • In-order Traversal: This type of traversal ensures that the tree's elements are processed in a strictly increasing order, which aligns perfectly with the sequential access provided by an iterator. The in-order traversal can be typically implemented using recursion or a stack.

Steps to Approach:

  1. Initialization:
    • Convert the given tree into a flat structure (like an array or list) representing the in-order traversal. This preprocessing step can be achieved by leveraging a stack where we iteratively push left children, thereby ensuring that we process the smallest element first.
  2. hasNext() and next():
    • For hasNext(), simply check whether there are unvisited elements after the current index.
    • For next(), move the internal cursor to the right, and return the value at the new cursor position.
  3. hasPrev() and prev():
    • For hasPrev(), verify that the cursor is not at the very start of the in-order traversal.
    • For prev(), move the cursor left, returning the value at the cursor.

Effectiveness of Approach:

  • The use of a list or array to store in-order elements converts potentially deep tree traversal to simple index operations on an array, enhancing access times for next() and prev() to constant time, O(1).
  • Space complexity becomes O(n) due to the storage of the entire tree's nodes in a sequential structure.
  • This direct approach conforms to the maximum constraint of operations, efficiently managing repeated calls to navigation methods.

Given the constraints provided which include very high operation counts (up to 105) but with individual operations bounded by constant time, this approach is effective in providing timely responses to iterator queries.

Solutions

  • Java
  • Python
java
class BSTTraversal {

    Deque<TreeNode> nodeStack;
    List<Integer> values;
    TreeNode currNode;
    int index;

    public BSTTraversal(TreeNode root) {
        currNode = root;
        nodeStack = new ArrayDeque();
        values = new ArrayList();
        index = -1;
    }
    
    public boolean hasNext() {
        return !nodeStack.isEmpty() || currNode != null || index < values.size() - 1;
    }
    
    public int next() {
        ++index;
        if (index == values.size()) {
            while (currNode != null) {
                nodeStack.push(currNode);
                currNode = currNode.left;                
            }
            TreeNode nextNode = nodeStack.pop();
            currNode = nextNode.right;
        
            values.add(nextNode.val);
        }
            
        return values.get(index);
    }
    
    public boolean hasPrev() {
        return index > 0;
    }
    
    public int prev() {
        --index;
        return values.get(index);
    }
}

Implement a Binary Search Tree (BST) iterator class in Java that provides forward and backward iteration over the elements of a BST. Below outlines your key implementation steps:

  1. Define essential class members:

    • Use Deque<TreeNode> for nodeStack to keep track of the nodes as you perform an in-order traversal.
    • List<Integer> values holds the elements after they have been accessed for easy retrieval in both directions.
    • TreeNode currNode maintains the current position in the BST.
    • int index keeps track of the current position in the list values.
  2. Initialize your iterator with the BSTTraversal(TreeNode root) constructor:

    • Start with currNode set to the root of the BST.
    • Initialize nodeStack as an ArrayDeque.
    • Set values as a new ArrayList.
    • Start index at -1 to indicate initial position before the first element.
  3. Implement hasNext() method:

    • Returns true if there are unvisited nodes in the stack, or if the current node is not null, or if there are more elements to visit in values.
  4. Implement next() method:

    • Increment index. Check if index equals the size of values to determine if traversal to fetch the next value is needed.
    • Traverse to the next in-order node if required by moving left till possible, then accessing node, moving to right.
    • Store each accessed node's value in values.
    • Return the current value from values using the updated index.
  5. Implement hasPrev() method:

    • Returns true if index is greater than 0, indicating there is a previous element.
  6. Implement prev() method:

    • Decrement index and return the value at the new index from values.

By following these detailed steps, you create a robust implementation of a BST iterator that can move both forward and backward through the elements of a tree, providing flexibility in traversing the data structure.

python
class BinarySearchTreeIterator:

    def __init__(self, root: TreeNode):
        self.current = root
        self.nodes_stack, self.values = [], []
        self.index = -1

    def hasNext(self) -> bool:
        return self.nodes_stack or self.current or self.index < len(self.values) - 1

    def next(self) -> int:
        self.index += 1
        
        if self.index == len(self.values):
            while self.current:
                self.nodes_stack.append(self.current)
                self.current = self.current.left
            node = self.nodes_stack.pop()
            self.current = node.right
            self.values.append(node.val)
        
        return self.values[self.index]

    def hasPrev(self) -> bool:
        return self.index > 0

    def prev(self) -> int:
        self.index -= 1
        return self.values[self.index]

The provided Python code defines a class BinarySearchTreeIterator to efficiently iterate over the elements of a binary search tree (BST) with the additional ability to move backwards. This iterator supports four operations: checking the presence of a next or previous element (hasNext and hasPrev), and retrieving the next or previous element (next and prev).

  • Initialization:

    • The constructor __init__ initializes with a given BST root. It also sets up a list nodes_stack for node traversal, a list values for storing accessed node values, and an index to track the current position in the values list.
  • hasNext Method:

    • This method checks if there are any nodes or values remaining to be accessed. It returns True if traversal can proceed forward, by examining the stack, current node, or index relative to the length of values.
  • next Method:

    • This method advances the iterator to the next element. It first increments the index. If the index reaches the current length of the values, the method processes nodes from the stack (implementing in-order traversal) until all direct and right-descendant nodes of the current node are accessed.
    • The accessed node's value is appended to values and then returned.
  • hasPrev Method:

    • This method checks the possibility of moving backwards by checking if the index is greater than 0. It returns True if the iterator can move to a previous element.
  • prev Method:

    • This method retrieves the previous element by decrementing the index and returning the value at the new index position from values.

This implementation allows efficient iteration over a BST in forward and backward directions, providing flexibility in navigation while ensuring that every node is processed at most twice.

Comments

No comments yet.