Algorithm

Construct Tree from Preorder and Inorder

Trees Pattern

Construct Binary Tree from Preorder and Inorder Traversal

You are given two arrays holding the same set of values: preorder, the values in the order a pre-order traversal produces them, and inorder, the values an in-order traversal produces. Rebuild the original binary tree and return its root. All values are distinct, and the two arrays are guaranteed to describe one consistent tree — which, given these two orders together, is unique.

CONSTRAINTS
  • 1 <= preorder.length <= 3000
  • inorder.length == preorder.length
  • All values in the tree are unique
  • preorder and inorder are guaranteed to be valid traversals of the same tree
EXAMPLE 1
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]
Pre-order names 3 as the root; in-order then shows exactly one value (9) sitting before it, so the left subtree holds one node and the remaining three belong on the right.
EXAMPLE 2
Input: preorder = [1,2], inorder = [2,1]
Output: [1,2]
2 appears before 1 in the in-order list, which places it on 1's left. Pre-order alone could not have told these two shapes apart.
EXAMPLE 3
Input: preorder = [1,2], inorder = [1,2]
Output: [1,null,2]
Identical pre-order to the previous case, but now 2 comes after 1 in-order, so it is the right child instead. The in-order array is carrying all the shape information.
EXAMPLE 4
Input: preorder = [1,2,3], inorder = [3,2,1]
Output: [1,2,null,3]
Every value precedes the one before it in-order, which forces a chain leaning entirely to the left.
Why are two traversals needed — is one not enough?
One is not enough. Pre-order [1,2] fits both a left child and a right child, so the shape is ambiguous. In-order pins down which side each value falls on.
Are the values guaranteed unique?
Yes, and the whole approach depends on it: locating the root inside the in-order array requires that value to appear exactly once. With duplicates the input would not even determine a single tree.
Would in-order plus post-order also work?
Yes — post-order's last element is the root instead of pre-order's first. In-order plus *level-order* works too. The one pairing that fails is pre-order plus post-order, which cannot distinguish a lone left child from a lone right child.
Can I assume the input is consistent?
Yes here. Validating that two arrays really describe some tree is a separate, harder question you would want to raise if the guarantee were dropped.

Until now we have been reading trees. This problem runs the machine backwards: given two readings, rebuild the tree that produced them. The first thing to settle is why two readings are needed at all.

One traversal is not enough, and here is the proof

Take the pre-order list [1, 2]. Pre-order visits the node, then the left subtree, then the right, so 1 is certainly the root and 2 is certainly its only child — but on which side? A tree with 2 as a left child gives pre-order [1, 2]. A tree with 2 as a right child gives pre-order [1, 2] as well. The two shapes are genuinely indistinguishable, so no algorithm can recover the tree from pre-order alone.

In-order alone fails too, and worse: [2, 1] is consistent with 2 being 1's left child, or with 1 being 2's right child, and with many larger shapes besides. Neither reading is enough on its own.

Now put them together. Pre-order says which node is the root. In-order says, for a known root, which values fall to its left and which to its right — because in-order visits the entire left subtree, then the node, then the entire right subtree, so the root's position in that list is precisely the boundary between the two sides. Between them the ambiguity vanishes: [1,2] with in-order [2,1] must be a left child, and with in-order [1,2] must be a right child.

Each array answers a different question

State the division of labour plainly, because it is the whole solution:

- Pre-order tells you who the root is. It is the first element of whatever segment you are looking at.
- In-order tells you how the rest splits. Find that root value in the in-order segment; everything before it belongs to the left subtree, everything after to the right.

And once the split is known, the sizes are known. If the in-order split says the left subtree contains k values, then in the pre-order segment — which lists the root, then all of the left subtree, then all of the right — the k entries immediately after the root are exactly the left subtree's, and the remainder is the right subtree's. Both arrays can now be cut into matching pieces, and each pair of pieces is a smaller instance of the identical problem. That is the recursion.

Building the parent before its children is what lets us hand back a finished node with both links attached, so the construction runs top-down — pre-order in shape as well as in name.

python
pos = {v: i for i, v in enumerate(inorder)}   # value -> its index in inorder, built once

def build(pre_lo, pre_hi, in_lo, in_hi):      # both ranges inclusive
    if pre_lo > pre_hi:
        return None                            # an empty segment is an empty subtree
    root_val = preorder[pre_lo]                # pre-order: the root comes first
    root = Node(root_val)
    mid = pos[root_val]                        # in-order: where the split falls
    left_size = mid - in_lo                    # how many values sit on the left
    root.left  = build(pre_lo + 1, pre_lo + left_size, in_lo, mid - 1)
    root.right = build(pre_lo + left_size + 1, pre_hi, mid + 1, in_hi)
    return root

return build(0, len(preorder) - 1, 0, len(inorder) - 1)
Crucial Noteleft_size is what keeps the two arrays in step, and it must be counted from in_lo, not from 0. Inside a recursive call the in-order segment is a slice of the original array, so the root's absolute index mid means nothing on its own — only its offset within the current segment does. Getting this wrong produces subtrees of the wrong size, which usually shows up as a crash or a silently mangled tree rather than an obvious error. Derive the pre-order boundaries from left_size too, and never guess them: the left segment starts one past the root and runs left_size entries; the right takes everything after that.
Finding the split cheaply

Locating the root inside the in-order segment by scanning costs up to O(N) per node, and with N nodes that is O(N²) — 9 million steps at the 3000-node limit, and the scan repeats work at every level. But the in-order array never changes, and every value is unique, so each value's position can be recorded once, up front, in a hash map from value to index. Lookups then take constant time regardless of the array's size, and the total drops to O(N).

This is the same trade made in Two Sum: spend O(N) memory to remember positions, and a repeated linear search collapses into a single constant-time question. The uniqueness guarantee is what licenses it — with duplicate values a single map entry could not identify the right occurrence, and indeed the input would no longer determine one tree.

Worked example:preorder = [3, 9, 20, 15, 7], inorder = [9, 3, 15, 20, 7]
- The whole range. Root is preorder[0] = 3. In-order puts 3 at index 1, so left_size = 1 — one value (9) on the left, three (15, 20, 7) on the right.
- Left call: pre-order slice [9], in-order slice [9]. Root 9, both sides empty, so a leaf.
- Right call: pre-order slice [20, 15, 7], in-order slice [15, 20, 7]. Root is 20; in-order puts 20 in the middle, so left_size = 1 — the value 15 on the left, 7 on the right.
- From the pre-order slice, the one entry after 20 is 15, so 15 is the left child; the remaining 7 is the right child. Both are leaves.
- Result: 3 with children 9 and 20; 20 with children 15 and 7.

Notice how the pre-order slice [20, 15, 7] is not itself in any useful order for the split — 15 and 7 are neighbours there with no marker between them. It was the in-order slice that said "one value goes left," and only then did the pre-order slice know where to cut.

Cost, and the shape to recognise

Every node is created exactly once, and with the map each creation is constant work: O(N) time. Space is O(N) for the map plus O(H) for the recursion, so O(N) overall. Without the map it degrades to O(N²), which on a chain-shaped tree is the worst case.

The reusable idea is divide and conquer keyed on a pivot: identify the element that splits the problem, use a second source of information to find where the split falls, and recurse on the two halves with carefully synchronised boundaries. It is the same skeleton as quicksort's partition and as binary search's midpoint, and here it is applied to reconstruction rather than sorting. The very next problem swaps pre-order for post-order — where the root is the last element rather than the first, so the recursion builds the right subtree before the left — and Serialize and Deserialize goes further, showing that with null markers included a single traversal becomes sufficient after all.

Interactive Strategy Visualization

Preorder + Inorder

Recursive Slicing Logic

Preorder Pick
3
9
20
15
7
Inorder Slicing
9
3
15
20
7
LEFT SUBTREE
ROOT
RIGHT SUBTREE
Subtree State
Starting construction. Look at first element of preorder.
CODE ANCHOR
rootNode = new Node(preorder[idx++]);
O(N²) Scan In-order For Each Root
O(N) Time · O(N) Space Hash Map Index Lookup