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:
[1, 105].0 <= Node.val <= 106105 calls will be made to hasNext, and next.Follow up:
next() and hasNext() to run in average O(1) time and use O(h) memory, where h is the height of the tree?When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach for iterating through a Binary Search Tree (BST) involves getting all the values first. We essentially extract all the node values from the tree into a list upfront, then use that list to simulate the iterator's behavior.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class BSTIterator:
def __init__(self, root: TreeNode):
self.sorted_values = []
self.current_index = 0
self.inorder_traversal(root)
def inorder_traversal(self, root: TreeNode):
if root:
self.inorder_traversal(root.left)
self.sorted_values.append(root.val)
self.inorder_traversal(root.right)
def next(self) -> int:
# Return the next smallest element
next_smallest_element = self.sorted_values[self.current_index]
self.current_index += 1
return next_smallest_element
def hasNext(self) -> bool:
# Check if there is another element
return self.current_index < len(self.sorted_values)The best way to go through a binary search tree 'in order' (smallest to largest) without extra memory is to use a stack to keep track of our progress. We'll essentially simulate the recursive inorder traversal iteratively, keeping track of the path we've taken.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class BSTIterator:
def __init__(self, root: TreeNode):
self.stack_of_nodes = []
self._push_left(root)
def _push_left(self, node: TreeNode):
# Traverse left, adding nodes to stack
while node:
self.stack_of_nodes.append(node)
node = node.left
def next(self) -> int:
# Pop the smallest, prepare for the next
current_node = self.stack_of_nodes.pop()
# If there's a right, push its left children
if current_node.right:
self._push_left(current_node.right)
return current_node.val
def hasNext(self) -> bool:
# True if there's still nodes in stack
return len(self.stack_of_nodes) > 0
# Your BSTIterator object will be instantiated and called as such:
# bSTIterator = BSTIterator(root)
# while bSTIterator.hasNext():
# print(bSTIterator.next())| Case | How to Handle |
|---|---|
| Root is null | The constructor should initialize an empty stack, and hasNext() should return false. |
| BST with only a root node | hasNext() should return true initially, next() should return the root's value once, and then hasNext() should return false. |
| Skewed BST (left-leaning or right-leaning) | The algorithm's space complexity will depend on the height of the tree, so a skewed tree will result in O(N) space usage (where N is the number of nodes). |
| BST with duplicate values | The in-order traversal will still correctly return the nodes in sorted order, including duplicates. |
| Large BST (many nodes) | The iterative in-order traversal using a stack should be efficient enough, and its space consumption depends on the height of the tree, which will be O(log N) for a balanced BST and O(N) for a skewed BST. |
| BST containing negative and/or zero values | The algorithm works correctly with negative and zero values since it only relies on the BST property (left < node < right). |
| Extremely large or small integer values in nodes | Ensure the language's integer type can handle the node values without overflow or underflow during comparisons. |
| Calling next() after hasNext() returns false | Define the behavior, such as throwing an exception or returning null, when calling next() on an exhausted iterator. |