105 · Construct Binary Tree from Preorder and Inorder
Preorder gives you the root; inorder tells you how much of what follows belongs to the left. The index map plus the arithmetic for the two sub-ranges is the entire problem — and it is always an off-by-one.
1 — The problem
Rebuild the tree from its preorder and inorder traversals, given distinct values. Two facts do all the work:
| Traversal | Shape | What it tells you |
|---|---|---|
| Preorder | root · [left subtree] · [right subtree] | The first element of any range is that subtree's root. Nothing else — the boundary between the two subtrees is invisible here. |
| Inorder | [left subtree] · root · [right subtree] | Once you know the root's value, its position splits the range, and the left part's length is what preorder was missing. |
So the two traversals are complementary: preorder names the root, inorder sizes the halves. Either alone is ambiguous — which is exactly what LC 889 demonstrates, and why "preorder + postorder" does not determine a unique tree.
Finding the root's position by scanning inorder is O(n) per node and makes the whole thing
O(n²). Build a value → index map once, up front, and every
lookup is O(1).
2 — Two ranges, shrinking together
Follow the leftSize value. It is computed from inorder and then spent on preorder — that hand-off is the mechanism, and every index in lines 7 and 8 is derived from it.
2.1 Deriving the four indices, not memorising them
Write out the preorder range as three labelled pieces and every bound reads off directly:
Piece of pre[preL..preR] | Occupies | Because |
|---|---|---|
| the root | preL | preorder visits the root first |
left subtree, leftSize elements | preL + 1 … preL + leftSize | it starts right after the root and has that many nodes |
| right subtree, the rest | preL + leftSize + 1 … preR | whatever remains |
And the inorder range splits at k: left is
inL … k-1, right is k+1 … inR.
Six bounds, all forced. The two that get written wrong most often:
preL + leftSizeas the left subtree's last index — notpreL + leftSize - 1. The+1for the root and the-1for an inclusive bound cancel. Verify it on a one-node left subtree:leftSize = 1should give the single indexpreL+1 … preL+1. It does.leftSize = k - inL— notk. Usingkworks at the root, whereinL = 0, and fails everywhere else. This is the bug that passes the first sample and nothing after it.
2.2 The consumed-pointer variant
Because preorder is consumed strictly left to right, the preL
parameter can be replaced by one shared cursor, leaving only the inorder range:
Java 21Shared preorder cursor — fewer indices to get wrong.9 lines
private int pre = 0;
private TreeNode build(int inL, int inR) {
if (inL > inR) return null;
int v = preorder[pre++]; // consume in preorder order
int k = idx.get(v);
TreeNode nd = new TreeNode(v);
nd.left = build(inL, k - 1); // LEFT first — preorder demands it
nd.right = build(k + 1, inR);
return nd;
}Two indices instead of four, and leftSize disappears entirely. The
price is that the cursor is shared mutable state, so the recursion order becomes load-bearing:
left must be built before right, because that is the order preorder stored them.
LC
106 is the same idea with the cursor running backwards and the calls reversed — write
them one after the other or they will fuse in memory.
3 — Complexity and edge cases
- Time O(n) with the index map, O(n²) if you scan inorder for each root. Space O(n) for the map plus O(h) for the recursion.
- Empty input:
preL > preRon the first call returns null. - Single node:
leftSize = 0, both recursive calls hit the guard immediately. - A straight line (every node has one child) is the worst case for recursion depth — O(n) stack — and the best test of the range arithmetic, because one side is always empty.
- Duplicate values break the approach outright: the index map becomes ambiguous and the tree is not uniquely determined. LeetCode guarantees distinct values; a follow-up asking about duplicates is asking you to notice that it is unanswerable.
- Common bug: passing
kwherek - inLis meant, as above.
4 — Reference implementation
Java 21Explicit four-index version, matching the visualizer.16 lines
private int[] pre;
private Map<Integer, Integer> idx = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
pre = preorder;
for (int i = 0; i < inorder.length; i++) idx.put(inorder[i], i);
return build(0, preorder.length - 1, 0, inorder.length - 1);
}
private TreeNode build(int preL, int preR, int inL, int inR) {
if (preL > preR) return null;
int rootVal = pre[preL];
int k = idx.get(rootVal); // root's seat in inorder
int leftSize = k - inL; // how many nodes are to its left
TreeNode nd = new TreeNode(rootVal);
nd.left = build(preL + 1, preL + leftSize, inL, k - 1);
nd.right = build(preL + leftSize + 1, preR, k + 1, inR);
return nd;
}