We're given an array that represents the preorder traversal of a BST, and we need to reconstruct the original tree. Preorder visits the root first, then the left subtree, then the right subtree, so the first element of the array is always the root.
The BST property determines everything else. All values smaller than the root belong to the left subtree, and all values larger belong to the right subtree. Because preorder lists the entire left subtree before the right subtree, the array for any subtree has the shape [root, left subtree values, right subtree values]. The first element greater than the root marks where the right subtree begins, and the same structure repeats inside each half.
1 <= preorder.length <= 100 → With n up to 100, even an O(n^2) approach passes. There is room for brute force, but O(n) is achievable.1 <= preorder[i] <= 1000 → Values fit comfortably in 32-bit integers, so the maximum integer value can serve as a sentinel upper bound.The array structure from the problem analysis translates directly into a recursive algorithm. Take the first element of the current range as the root, scan forward for the first element greater than it, and split the rest there: everything before the split is the left subtree's preorder, everything from the split onward is the right subtree's preorder. Build each side the same way.
The linear scan for the split point is the bottleneck. The next approach replaces it with a binary search.
The idea is the same as Approach 1: take the first element as the root, split the rest into left and right subtree portions, and recurse. The difference is how we find the split point.
Within any subtree's range, all left-subtree elements are less than the root and all right-subtree elements are greater, so the range after the root is a block of smaller values followed by a block of larger values. That monotone less-than/greater-than transition is a valid target for binary search: find the first element that exceeds the root value. This reduces the split-point search from O(n) to O(log n).
Both approaches so far spend time locating split points. The next approach removes that search entirely: it processes the preorder array left to right and uses value bounds to decide where each element belongs.
Instead of finding split points, we process the preorder array left to right using a single index, and we pass an upper bound to each recursive call: the maximum value a node in that subtree is allowed to have.
When we recurse into the left subtree of a node with value 8, the upper bound becomes 8, since all left-subtree values must be less than 8. When we recurse into the right subtree, the upper bound stays whatever the parent received, because the constraint from higher up the tree still applies.
At each step, we compare the current element against the bound. If it is less than the bound, it belongs in the current subtree: create a node and advance the index. If it exceeds the bound, the current subtree is complete: return null without consuming the element, and a caller higher up the recursion will place it. Each element is consumed exactly once, so construction takes O(n).
At any moment during construction, several subtrees are open (started but not finished), and their upper bounds are nested: each left-subtree bound is tighter than every bound above it. The next array element x is tested against the innermost bound first. If x is below that bound, x must belong to the innermost open subtree, because preorder lists every node of a subtree before any node that follows the subtree. If x exceeds the bound, the innermost subtree cannot contain x (all of its values lie below the bound), so that subtree is finished, the call returns, and x is retested against the next enclosing bound. Each element is therefore placed in the deepest open subtree that can legally contain it, which is the position preorder dictates.
Loading animation...
The recursion in Approach 3 tracks open subtrees on the call stack. An explicit stack does the same bookkeeping iteratively, in the same O(n) time.
Maintain a stack of nodes that are still waiting for a right child. Values on the stack strictly decrease from bottom to top: a new node is pushed either as the left child of the current top, so it is smaller, or after the pop loop has stopped at a node with a larger value. The top of the stack is always the most recently processed element. For each new value v from the array:
Either way, push the new node, since it may still receive a right child later. Each node is pushed once and popped at most once, so the whole construction is O(n) with no recursion.