Construct Tree from Inorder and Postorder
You are given inorder, the values of a binary tree in the order an in-order traversal produces them, and postorder, the values in post-order. Rebuild the tree and return its root. All values are distinct, and the two arrays are guaranteed to come from the same tree, which they determine uniquely.
- 1 <= inorder.length <= 3000
- postorder.length == inorder.length
- All values in the tree are unique
- inorder and postorder are guaranteed to be valid traversals of the same tree
inorder = [9,3,15,20,7], postorder = [9,15,7,20,3][3,9,20,null,null,15,7]inorder = [2,1], postorder = [2,1][1,2]inorder = [1,2], postorder = [2,1][1,null,2]inorder = [1], postorder = [1][1]Again two readings of one tree — this time in-order and post-order — and the job is to rebuild it.
Post-order walks left subtree → right subtree → node, so the root is the last value. In-order still does the splitting: wherever the root sits, everything before it is the left subtree, everything after is the right.
post: [ 9 15 7 20 | 3 ] 3 is the root (comes LAST)
^root
in: [ 9 | 3 | 15 20 7 ] 3 sits here -> LEFT = {9}, RIGHT = {15,20,7}
^rootThe tidy way to eat post-order: one index walking backwards from the end, taking one root per call. But look at what sits just before the root inside a segment:
segment: [ ...left... ...right... root ]
^ step back from root lands HERE = right subtreeStepping back lands you in the right subtree, not the left. So the recursive calls must go right, then left — otherwise the backward index feeds right-subtree values into the left side.
pos = {v: i for i, v in enumerate(inorder)} # value -> index in inorder
idx = len(postorder) - 1 # walks backwards, one root per call
def build(lo, hi): # inclusive in-order range
nonlocal idx
if lo > hi:
return None # empty range -> no subtree
root = Node(postorder[idx])
idx -= 1 # consume this root
mid = pos[root.val] # where it splits the in-order range
root.right = build(mid + 1, hi) # RIGHT first — see the note
root.left = build(lo, mid - 1)
return root
return build(0, len(inorder) - 1)idx, the call order is what keeps everything aligned — there are no post-order boundaries being checked, so a mistake yields a plausible-looking (but silently mirrored) tree, not an error. Swap the two recursive lines and the right subtree's values become the left. The value-to-index map makes each split O(1); without it, scanning is O(N²). It relies on values being unique.inorder = [9, 3, 15, 20, 7], postorder = [9, 15, 7, 20, 3]. Follow idx from the back:
idx -> 3 : root 3, in [9 | 3 | 15 20 7] -> left {9}, right {15,20,7}
idx -> 20: build RIGHT first. root 20, in [15 | 20 | 7] -> left {15}, right {7}
idx -> 7 : right of 20 -> leaf 7
idx -> 15: left of 20 -> leaf 15
idx -> 9 : back to top, left of 3 -> leaf 9
result: 3
/ \
9 20
/ \
15 7The values idx visits — 3, 20, 7, 15, 9 — are post-order read backwards, which is root, right, left: exactly the order the recursion walks. O(N) time, O(N) space.
Inorder + Postorder
Recursive Slicing Logic