Trees · Binary Search Tree Operations

BST Construct From Preorder

Rebuilding a BST from a preorder sequence alone (no second traversal needed, unlike a general binary tree) by tracking a shrinking upper bound as recursion descends - a value can only continue as part of the current subtree while it stays under that bound.

  • 6 min read
  • 8 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: rebuilding from postorder
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

A BST can be rebuilt from its preorder list alone. You don't need a second traversal or any null markers, because the BST order already tells you where each subtree ends. This skill teaches the O(n) rebuild: one cursor that only moves forward, and a bound that says how large a value can be and still belong to the current subtree. It also covers the mirror-image rebuild from postorder.

A general binary tree can't be rebuilt from its preorder traversal alone: preorder tells you each subtree's root but not where its left subtree ends and its right subtree begins - you need inorder as well (see Binary Tree Reconstruction). A BST is different. Its inorder is just its values in sorted order, so the "where does the left subtree end" question already has an answer: at the first value too large to belong there.

We'll go from simple to more technical:

  1. The pattern: a shared cursor and an upper bound.
  2. A second flavor: rebuilding from postorder (read backward, lower bound, right first).
  3. Where this pays off: storing an ordered structure as one list.
  4. What breaks it, and why repeated insertion is the slow way.

The approach

Java

int idx = 0;

TreeNode bstFromPreorder(int[] preorder) {
    return build(preorder, Integer.MAX_VALUE);
}

TreeNode build(int[] preorder, int bound) {
    if (idx == preorder.length || preorder[idx] > bound) return null;   // out of values, or it belongs further up
    TreeNode root = new TreeNode(preorder[idx++]);
    root.left = build(preorder, root.val);     // left subtree: everything below root's own value
    root.right = build(preorder, bound);       // right subtree: everything still below the inherited bound
    return root;
}

Python

def bst_from_preorder(preorder: list[int]) -> TreeNode | None:
    idx = 0

    def build(bound: float) -> TreeNode | None:
        nonlocal idx
        if idx == len(preorder) or preorder[idx] > bound:
            return None                      # out of values, or it belongs further up
        root = TreeNode(preorder[idx])
        idx += 1
        root.left = build(root.val)          # left subtree: everything below root's own value
        root.right = build(bound)            # right subtree: everything still below the inherited bound
        return root

    return build(float("inf"))

The shared idx cursor only moves forward, reading values in the order preorder lists them - a node, then its whole left subtree, then its whole right subtree. The bound says how large a value may be and still belong to the current subtree. The left child gets the node's own value as its bound; the right child inherits the parent's bound. The first value that exceeds a bound ends that subtree and is handed back up to the first ancestor whose bound it fits.

Only an upper bound is ever needed. A value reaches a node's right subtree only after failing the left subtree's bound - so it's already known to be larger than the node, and the lower bound comes for free from the reading order.

A second flavor: rebuilding from postorder

Postorder lists the left subtree, then the right, then the node - the root comes last. So everything mirrors: read the cursor from the end, use a lower bound, and build the right subtree before the left:

Java

int idx;   // starts at postorder.length - 1

TreeNode build(int[] post, int lowerBound) {
    if (idx < 0 || post[idx] < lowerBound) return null;
    TreeNode root = new TreeNode(post[idx--]);
    root.right = build(post, root.val);        // walking backward, the right subtree comes next
    root.left = build(post, lowerBound);
    return root;
}

Python

def bst_from_postorder(post: list[int]) -> TreeNode | None:
    idx = len(post) - 1

    def build(lower_bound: float) -> TreeNode | None:
        nonlocal idx
        if idx < 0 or post[idx] < lower_bound:
            return None
        root = TreeNode(post[idx])
        idx -= 1
        root.right = build(root.val)         # walking backward, the right subtree comes next
        root.left = build(lower_bound)
        return root

    return build(float("-inf"))

Reading backward, postorder becomes "node, right subtree, left subtree" - preorder with the sides swapped. That's why the right child gets the node's value as its (lower) bound and is built first. Building left first, copied from the preorder version, consumes values that belong to the right subtree.

Where you'll see it

Serializing an ordered structure as a single traversal - and rebuilding it in linear time - is a direct payoff of keeping it ordered: a BST (or a sorted index) can be persisted as one preorder list with no null markers and no second traversal, where a general tree needs either null markers or two traversals.

Which tool for which job?

You needUse
A BST back from its preordera shared cursor and an upper bound, O(n)
A BST back from its postorderthe cursor read from the end, a lower bound, right subtree first
A general binary tree backpreorder plus inorder, or a traversal with null markers
A rebuild that can't overflow the stackthe iterative version with an explicit stack
A check that the list really came from a BSTboth bounds, then confirm the cursor reached the end

Common mistakes

MistakeWhat happensDo this instead
Passing the inherited bound to the left childlarger values end up on the left; the result isn't a BSTthe left child's bound is the node's own value
Building left first from postorderthe right subtree's values go leftread backward and build right first
Rebuilding by repeated insertionO(n * h), up to O(n^2)the bound-cursor rebuild, O(n)
Trusting the input to be a valid preorderan invalid list silently becomes a tree that isn't a BSTboth bounds, then check the cursor reached the end
Recursing on a sorted list of 100,000 valuesstack overflowthe iterative version with an explicit stack

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. Rebuilding from preorder 8 5 1 7 10 12: when the left subtree of 5 is being built (bound 5), which values does it take, and what stops it?

Show answer

It takes 1 (1 < 5), and 1's own subtrees end immediately because 7 exceeds their bounds. Then 7 > 5 stops 5's left subtree; 7 fits 5's inherited bound (8), so it becomes 5's right child. Next, 10 exceeds 8, so it's handed back up to the root and becomes 8's right child, and 12 becomes 10's right child.

2. Why does the preorder rebuild need only an upper bound, never a lower one?

Show answer

Because of the reading order. Preorder presents a node's whole left subtree before its right subtree, and the left subtree stops at the first value larger than the node - so the only values that ever reach the right subtree are already larger than the node. The lower bound is enforced by the cursor, not by a check.

3. What changes when rebuilding from postorder instead of preorder?

Show answer

Three things flip together: the cursor starts at the end and moves backward, the bound becomes a lower bound (the node's value for its right child), and the right subtree is built before the left - because postorder read backward is "node, right, left". Keeping preorder's left-first build order hands the right subtree's values to the left.

Key takeaways

  • A BST can be rebuilt from one traversal because its inorder is simply its sorted values - no second traversal needed.
  • One shared cursor plus a bound decides where every value goes: the left child is bounded by the node's value, the right child inherits the parent's bound.
  • Preorder needs only an upper bound - the reading order enforces the lower one.
  • From postorder, everything mirrors: read backward, use a lower bound, build right before left.
  • The bound-cursor rebuild is O(n); repeated insertion is O(n * h), up to O(n^2).

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
A BST from preordera cursor and an upper bound
A BST from postorderread backward, a lower bound, right first
No recursiona monotonic stack
Validationboth bounds, then check the cursor reached the end
A general treepreorder plus inorder, or null markers

Snippets worth knowing by heart

Java

TreeNode build(int[] pre, int bound) {
    if (idx == pre.length || pre[idx] > bound) return null;
    TreeNode root = new TreeNode(pre[idx++]);
    root.left = build(pre, root.val);
    root.right = build(pre, bound);
    return root;
}

Python

def build(lower_bound):                  # from postorder, idx starts at the end
    nonlocal idx
    if idx < 0 or post[idx] < lower_bound:
        return None
    root = TreeNode(post[idx]); idx -= 1
    root.right = build(root.val)
    root.left = build(lower_bound)
    return root

The traps

  • The left child inherits the parent's bound. It should get the node's own value.
  • Postorder built left first. Build the right subtree first.
  • Repeated insertion is O(n^2) on sorted input.
  • Invalid input silently becomes a tree that isn't a BST.

Staff-level points, one line each

  • Each value is consumed once: O(n) for any shape. Insertion of 2,000 sorted keys made 1,999,000 comparisons.
  • Recursion on a sorted list: Python raised RecursionError at 2,000 keys; Java overflowed the stack at 100,000.
  • Monotonic stack: pop the smaller values, attach as the right child of the last one popped. It rebuilt 100,000 sorted keys without trouble.
  • [5, 8, 3] became 5 → right 8 → left 3 with no error. Two bounds plus a cursor check catch it.
  • A BST serializes as n values; a general tree needs 2n + 1 tokens with null markers.

Say it in an interview

  • "A BST's inorder is just its sorted values, so preorder alone is enough."
  • "The left child is bounded by the node's value; the right child inherits the parent's bound."
  • "Each value is placed exactly once, so it's O(n)."
  • "For deep inputs I'd use the stack version, and for untrusted input I'd carry both bounds."

Practice

This skill comes with 8 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice BST Construct From Preorder free

Construct BST from Preorder Traversal

MediumProCoding problemJava · Python

Given an array representing the preorder traversal of a binary search tree (all values distinct), reconstruct and return the tree. Unlike reconstructing a general binary tree, no second traversal is needed: the BST ordering property alone determines the structure.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Construct BST from Postorder Traversal

MediumProCoding problemJava · Python

Given an array representing the postorder traversal of a binary search tree (all values distinct), reconstruct and return the tree - in O(n), without sorting and without a second traversal.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice BST Construct From Preorder free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime