We are given the inorder and postorder traversals of a binary tree and have to rebuild the tree.
Each traversal contributes a different piece of structural information. Postorder visits left subtree, right subtree, root, so the last element of the postorder array is the root of the whole tree. Inorder visits left subtree, root, right subtree, so once we locate that root value in the inorder array, everything before it belongs to the left subtree and everything after it belongs to the right subtree.
This splits the problem into two smaller copies of itself: rebuild the left subtree from its portion of both arrays, rebuild the right subtree from its portion, and attach both to the root. Splitting the inorder array is direct because the root's position marks the boundary. Splitting the postorder array requires knowing how many nodes the left subtree contains, and the inorder split provides that count.
1 <= inorder.length <= 3000 → n is small enough that an O(n^2) solution passes, but O(n) is reachable with a single hash map.-3000 <= inorder[i], postorder[i] <= 3000 → Values fit in a 32-bit integer, and any number outside this range (such as 3001) can act as a sentinel that never matches a node value.The recursion follows directly from how the two traversals encode structure. The last element of any postorder segment is that subtree's root. Finding the root's position in the corresponding inorder segment splits the segment into left and right subtrees. Repeat the same step on each half until the segments are empty.
Splitting the postorder segment takes one extra calculation. If the root sits at index rootIndex in the inorder array and the current inorder range starts at inStart, the left subtree contains leftSize = rootIndex - inStart nodes. Postorder lists the entire left subtree before the right subtree, so those nodes occupy the first leftSize positions of the postorder segment: [postStart, postStart + leftSize - 1] holds the left subtree, [postStart + leftSize, postEnd - 1] holds the right subtree, and the root itself sits at postEnd.
Input: inorder = [9, 3, 15, 20, 7], postorder = [9, 15, 7, 20, 3].
The recursion itself touches each node once; only the repeated linear searches push the cost to O(n^2). The position of every value in the inorder array never changes between calls, so those searches recompute the same information. The next approach computes all positions once, up front.
Since all values are unique, a hash map from value to inorder index, built once before the recursion starts, turns every root lookup from O(n) into O(1). That makes the entire algorithm O(n).
The recursive logic stays the same: the last element of the current postorder segment is the root, its position in inorder determines the left subtree size, and both arrays are split accordingly. The only change is that the lookup is now a hash map query instead of a loop.
rootIndex - inStart.Input: inorder = [9, 3, 15, 20, 7], postorder = [9, 15, 7, 20, 3].
O(n) time cannot be improved, since every node has to be created. The hash map, though, costs O(n) extra space on top of the recursion stack. A different recursion order removes it.
Reading the postorder array from the end produces: root, then the right subtree, then the left subtree (each subtree appearing in this same reversed order). A recursion that always builds the right child before the left child can therefore consume postorder back to front with a single moving index, one element per node, with no range arithmetic and no hash map.
What remains is detecting where each subtree ends. A second index walks the inorder array from the end. Reversed inorder is: right subtree, root, left subtree. While a node's right subtree is being built, the elements consumed from reversed inorder are exactly that right subtree, so the moment the inorder pointer reaches the node's own value, the right subtree is finished. Each recursive call therefore carries a stop value: create nodes until the current inorder element equals stop, then return null. The right child's call uses the parent's value as its stop. The left child's call reuses the stop inherited from the parent, because in reversed inorder the left subtree runs up to the same boundary that delimited the parent's whole subtree.
The initial call needs a stop value that matches nothing. Values are bounded by 3000, so 3001 works as a sentinel.
postIndex and inIndex to the last positions of their arrays.postIndex is below 0, or the current inorder element equals the stop value, return null.postorder[postIndex] and decrement postIndex.inIndex. This consumes the node's own value in the inorder array.Input: inorder = [9, 3, 15, 20, 7], postorder = [9, 15, 7, 20, 3]. Both pointers start at index 4.