In a BST preorder traversal, we visit root, then left subtree, then right subtree. When we move to a right subtree, all subsequent values must be greater than the ancestors we are leaving behind. The key insight is to use a decreasing stack to track ancestors. When we encounter a larger value, we pop smaller ancestors and update the minimum limit, as we are now in a right subtree.
min_limit to negative infinity.preorder sequence.min_limit to the popped value.min_limit, return false as it violates BST property.true.Where is the length of
preorder
We can optimize the stack approach by reusing the input array itself as our stack. Since we process elements left to right and the stack never grows larger than the elements we have processed, we can use the prefix of the preorder array to simulate the stack.
min_limit to negative infinity and use index i as the stack pointer.preorder sequence.i > 0 and preorder[i-1] is less than the current number, set min_limit to preorder[i-1] and decrement i.min_limit, return false.preorder[i] and increment i to simulate pushing onto the stack.true if all numbers are processed without violations.class Solution:
def verifyPreorder(self, preorder: List[int]) -> bool:
min_limit = float('-inf')
i = 0
for num in preorder:
while i > 0 and preorder[i - 1] < num:
min_limit = preorder[i - 1]
i -= 1
if num <= min_limit:
return False
preorder[i] = num
i += 1
return TrueTime complexity:
Space complexity: auxiliary
A common misconception is that modifying an input array for use in an algorithm leads to an space complexity. In reality, you are still using space, but auxiliary space.
Because we are modifying the input to directly use in the algorithm, we must count it as part of the space complexity. However, we are not using any auxiliary space other than a few integers.
The exception to this is in-place algorithms where the input is also returned as the output. For example: sorting algorithms.
Where is the length of
preorder
We can verify the preorder sequence by simulating the construction of the BST. Each recursive call attempts to build a subtree within given bounds. The key insight is that for a valid preorder sequence, we can greedily consume elements that fall within the current subtree's valid range, recursively processing left and right subtrees.
preorder array.min_limit and max_limit as boundaries.true as all elements have been processed.false.max_limit as current value) and right subtree (with min_limit as current value).true if either the left or right subtree verification succeeds, allowing the sequence to be valid.class Solution:
def verifyPreorder(self, preorder: List[int]) -> bool:
def helper(i, min_limit, max_limit):
if i[0] == len(preorder):
return True
root = preorder[i[0]]
if not min_limit < root < max_limit:
return False
i[0] += 1
left = helper(i, min_limit, root)
right = helper(i, root, max_limit)
return left or right
return helper([0], float('-inf'), float('inf'))Where is the length of
preorder
When checking if a value violates BST constraints, you must use less than or equal (<=) for the minimum limit check, not just less than. BSTs typically do not allow duplicate values, so a value equal to an ancestor it should be greater than is invalid.
The minimum limit should only be updated when you pop elements from the stack (transitioning to a right subtree). A common mistake is updating the limit at the wrong time, such as when pushing elements. The limit represents the most recently left ancestor whose right subtree you are now in.
The stack should maintain a decreasing order from bottom to top. When you encounter a larger value, you pop smaller values to find the correct parent. Forgetting to maintain this invariant or popping incorrectly will cause the algorithm to fail on valid preorder sequences.