Verify Preorder is a Valid BST hands you a flat list of numbers and asks a structural question: could this sequence have come from a preorder traversal of some binary search tree? The elegant answer uses a stack and a single rising lower bound — and runs in one pass.
Problem. Given an array preorder, return true if it is a valid preorder traversal of a binary
search tree (all left descendants smaller, all right descendants larger), and false otherwise. Assume
all values are distinct.
Example: preorder = [5, 2, 6, 1, 3] → false. After we visit 6 (a right turn from 5), everything that
follows must exceed 5 — but 1 appears, which is impossible.
The slow way first
You could try to rebuild the tree: the first element is the root, then split the rest into the prefix smaller than the root (left subtree) and the suffix larger than it (right subtree), and recurse. That works, but each split scans the remaining array, giving O(n²) in the worst case (a skewed tree).
The question to ask: as I read the values left to right, what constraint does each new value have to obey? In preorder, once you finish a left subtree and step to the right, you can never see a value smaller than the parent you turned right from again. So there is a lower bound that only ever goes up.
The idea: a stack and a rising lower bound
Keep a stack of the ancestors whose left subtrees we are still inside, and a lower bound. For each val:
- If
val < lower, the sequence is impossible — returnfalse. - While the stack top is smaller than
val, we are turning right out of that node: pop it and raiselowerto its value. - Push
valand continue.
Each pop marks a right turn. The popped value becomes the new floor because nothing in a right subtree may dip below the node it hangs off of.
Walk through it
Step through the animation. The pointer scans [5, 2, 6, 1, 3]. We push 5, then 2 (a left child, smaller than the top). At 6 we pop both 2 and 5 — two right turns — so lower jumps to 5. Then 1 arrives: 1 is below lower = 5, which is impossible, so we return false.
Pseudocode
stack = empty
lower = -infinity
for each val in preorder:
if val < lower: # dipped below a right-turn floor
return false
while stack not empty and top of stack < val:
lower = pop the stack # turning right: raise the floor
push val onto stack
return trueThe Python solution
def verify_preorder(preorder):
stack = []
lower = float('-inf')
for val in preorder:
if val < lower:
return False
while stack and stack[-1] < val:
lower = stack.pop()
stack.append(val)
return Truestackholds the chain of ancestors we are still inside the left subtree of.loweris the floor every future value must clear; it starts at negative infinity.- Line 5 is the rejection: if
valhas slipped under the floor, no BST could produce this order. - Lines 7–8 are the right turns — popping ancestors smaller than
valand liftinglowerto the last one popped. - We push
valso it can later become a floor when we turn right out of it.
Complexity
| Case | Time | Notes |
|---|---|---|
| Rebuild + split | O(n²) (slow) | rescan on every split |
| Stack + lower bound | O(n) (moderate) | each value pushed and popped once |
O(n) (moderate)Every value is pushed once and popped at most once, so the total work across all the inner while loops is linear — an amortized O(n) pass. The stack uses O(n) space in the worst case (a fully left-leaning input).
When this pattern shows up
A monotonic stack plus a running bound is the go-to for validating or processing a traversal order: preorder/postorder validity, next-greater-element, and stack-based tree reconstruction all lean on popping smaller items to advance a frontier.
The bound only moves on a pop, never on a push. A common bug is updating lower when you push a left
child — that wrongly forbids the perfectly legal smaller values inside that left subtree.
Practice
For preorder = [5, 2, 6, 1, 3], what is lower right after we process the value 6, and why?
1. What does popping a value off the stack represent?
2. Why is the algorithm O(n) despite the inner while loop?
3. When do we return False?
4. What is the worst-case extra space?