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.
- 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
preorder = [3,9,20,15,7], inorder = [9,3,15,20,7][3,9,20,null,null,15,7]preorder = [1,2], inorder = [2,1][1,2]preorder = [1,2], inorder = [1,2][1,null,2]preorder = [1,2,3], inorder = [3,2,1][1,2,null,3]Two readings of one tree, and the job is to rebuild it. Neither reading alone is enough — together they pin down exactly one tree.
Pre-order [1, 2]: since pre-order is root-then-children, 1 is the root and 2 is its only child. But which side?
1 1
/ or \
2 2
both produce pre-order [1, 2]Pre-order can't separate them. The rescue is a second reading. In-order walks left subtree → node → right subtree, so wherever the root sits in the in-order list, everything before it is the left subtree and everything after is the right.
- Pre-order names the root — the first value of the current segment.
- In-order splits the rest — find that root in the in-order segment; values before it go left, values after go right.
pre: [ 3 | 9 | 20 15 7 ] 3 is the root (comes first)
^root
in: [ 9 | 3 | 15 20 7 ] 3 sits here -> LEFT = {9}, RIGHT = {15,20,7}
^rootOnce in-order says the left side holds k values, pre-order cuts to match: the k values right after the root are the left subtree, the rest are the right. Both arrays split into matching pieces, and each pair is the same problem again → recurse. The root is built before its children, so construction runs top-down.
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 # empty segment -> empty subtree
root_val = preorder[pre_lo] # pre-order: root comes first
root = Node(root_val)
mid = pos[root_val] # in-order: where it splits
left_size = mid - in_lo # how many values 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)left_size counts from in_lo, not from 0. Inside a recursive call the in-order segment is a slice, so the root's absolute index mid means nothing on its own — only its offset within the current segment does. Get this wrong and subtrees come out the wrong size.Scanning the in-order segment for each root costs O(N) per node → O(N²). But in-order never changes and every value is unique, so record each value's index once in a hash map up front. Lookups become O(1), and the whole build drops to O(N).
preorder = [3, 9, 20, 15, 7], inorder = [9, 3, 15, 20, 7]:
root 3: in-order [9 | 3 | 15 20 7] -> left {9}, right {15,20,7}
|
left = 9 (leaf) |
right subtree from pre [20 15 7], in [15 20 7]:
root 20: in [15 | 20 | 7] -> left {15}, right {7}
left = 15 (leaf), right = 7 (leaf)
result: 3
/ \
9 20
/ \
15 7Note the pre-order slice [20, 15, 7] gives no clue where 15 and 7 split — it was the in-order slice that said "one value goes left," and only then did pre-order know where to cut. O(N) time, O(N) space.
Preorder + Inorder
Recursive Slicing Logic