Hi👋SpeedAlgo • Deliberate Practice & Cognitive Ergonomics for Software Engineers

An interactive algorithm mastery and technical interview preparation platform published by Hi👋WebEnterprise. Built for senior and staff software engineers preparing for rigorous coding screens at top tech companies (FAANG/MAMAA).

13 Core Algorithmic Patterns & 225 Practice Problems

  • 1. Two Pointers (16 Paradigms, 43 Problems): Opposite ends, sorted pair sums, palindrome verification, trapping rain water, 3Sum, container with most water — includes the Sliding Window and Fast & Slow Pointers paradigms (contiguous subarray invariants, longest substrings, Floyd's cycle detection, linked list middle).
  • 2. Binary Search (9 Paradigms, 14 Problems): Monotonic predicate partitioning (feasible(x)), boundary search, rotated sorted arrays, square roots, minimum ship capacity, median of two sorted arrays, matrix median on value range.
  • 3. Bit Manipulation (5 Paradigms, 11 Problems): Counting set bits, XOR cancellation, bit reversal, bitmask pairing, maximum XOR with a bitwise trie.
  • 4. Math & Geometry (8 Paradigms, 14 Problems): Sieve of Eratosthenes, integer/roman conversions, modular arithmetic, geometric simulation.
  • 5. Tree/Graph Depth-First Search (15 Paradigms, 24 Problems): Path sums, lowest common ancestor, tree diameter, validating BSTs, tree DP (House Robber III, Binary Tree Cameras).
  • 6. Tree/Graph Breadth-First Search (5 Paradigms, 12 Problems): Level-order traversals, shortest path in unweighted graphs, zig-zag traversals, rotting oranges, word ladders.
  • 7. Graphs (12 Paradigms, 22 Problems): Topological sort, cycle detection, Dijkstra's algorithm, bipartite graph validation, network delay.
  • 8. Backtracking (6 Paradigms, 14 Problems): Subsets, permutations, combinations, constraint satisfaction, pruning, N-Queens, Sudoku solver.
  • 9. Dynamic Programming (13 Paradigms, 28 Problems): Memoization vs tabulation, 0/1 knapsack, unbounded knapsack, coin change, edit distance, longest common subsequence.
  • 10. Heap / Priority Queue (9 Paradigms, 11 Problems): Running medians, top-k elements, interval scheduling, IPO, k-way merges.
  • 11. Advanced Data Structures (7 Paradigms, 17 Problems): Trie (prefix tree), Union-Find (Disjoint Set Union), LRU Cache, LFU Cache, range-sum queries with updates.
  • 12. Stack (5 Paradigms, 9 Problems): Next greater element, largest rectangle in histogram, remove k digits, decode string, min stack.
  • 13. Intervals & Greedy (2 Paradigms, 6 Problems): Merge intervals, interval scheduling, meeting rooms by sweep line, jump game.

4-Stage Deliberate Practice Framework

  1. Stage 1 (Compare & Learn): Read the pattern's mental model, loop invariant and Big-O proof, compare the pattern's template with the problem's solution side by side, and use the step-by-step debugger. The solution can also be shown in C#, Java, TypeScript, C++, Go or Rust for almost every problem.
  2. Stage 2 (Active Recall): Rebuild the solution from memory in a code editor, with an invariant checklist and optional progressive hints, and check it against a judged test suite.
  3. Stage 3 (AI Interview Coach, coming soon): A mock-interview stage still in development; for now, students go straight from Stage 2 to Stage 4.
  4. Stage 4 (Solve on Your Own): Solve a new problem on your own against a 15-minute timer; your code is graded against a test suite. Python and TypeScript run in your browser and C# runs on our server (design problems grade in Python and TypeScript).

Equipped with a spaced-repetition review hub on a fixed 7-step schedule (1 day up to 90 days), a two-pane Studio workspace, and study notes.

Pricing, Access & Commercial Terms

  • Core Curriculum: 100% Free. No credit card required.
  • Coins: New accounts get 40 free coins once, the daily claim adds +20 coins once a day, and inviting a friend earns you +25 coins when they complete their first practice step. These are the only ways to earn coins.
  • AI Coach: Each AI coach question costs 1-5 coins, priced by the server according to its size.
  • BYOK (Bring Your Own Key): AI coaching is free with your own Google Gemini, OpenAI or OpenRouter API key (BYOK).
  • Extras: Coins also unlock extras for 24 hours: a Recall hint level (1 coin), a Solve solution reveal (2), a LeetCode problem card's solutions (1) and a pattern's extra canonical problems (1).
  • Payments & Refunds: Nothing on the site is sold for money: coins can only be earned, never bought. There are no subscriptions or charges, so there is nothing to refund.
  • Platform Operator: Hi👋WebEnterprise. Support & policies at hispeedalgo.com.
Skip to main content
Hi👋SpeedAlgo

Invariant-First Algorithmic Mastery

Arena
238Items
Theory Context•Depth-First Search (DFS)
MediumLC 105

Construct Binary Tree from Preorder and Inorder Traversal (LeetCode 105)

Target Frequency:AmazonMicrosoftBloomberg

A binary tree whose values are all different was walked twice, and each walk wrote its values into a list.

  • preorder lists every subtree as its root first, then its left subtree, then its right subtree.
  • inorder lists every subtree as its left subtree first, then its root, then its right subtree.

Both lists describe the same tree and hold the same values. Rebuild that tree and return its root.

Worked Examples

Example 1
Input:preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output:[3,9,20,null,null,15,7]
9315207
Explanation: 3 comes first in preorder, so it is the root. In inorder, only 9 sits left of 3, so 9 is the whole left subtree; 15, 20 and 7 sit right of it. Of those, 20 comes first in preorder, so it roots the right side, with 15 on its left and 7 on its right.
Example 2
Input:preorder = [-1], inorder = [-1]
Output:[-1]
-1
Explanation: One value makes a tree of one node.

⚖️Formal Constraints & Bounds

  • 1 <= preorder.length <= 3000

  • inorder.length == preorder.length

  • -3000 <= preorder[i], inorder[i] <= 3000

  • The values in preorder are distinct, and so are the values in inorder.

  • Every value of inorder also appears in preorder.

  • preorder is the tree's preorder traversal and inorder is the same tree's inorder traversal.

Deep-Dive & Conceptual Insights

Core Insight

Preorder gives each subtree's root before anything else in it, and inorder puts that root between its two subtrees. So the next unused preorder value is the root, its inorder index splits the range, and building the left side first keeps the preorder cursor on the right root for the right side.

Real-World Scenario & Production Applications

Tree-shaped data is often stored as a flat list and read back later: a listing of folders written parent-first is a preorder, and so is the order in which many serializers write an object tree. One order alone can't give back the shape; a second order, or a marker for every missing child, can. Restoring a saved tree from such listings is this problem.

Step-by-Step Execution Trace Table

Example 1, preorder = [3,9,20,15,7], inorder = [9,3,15,20,7], so where = {9: 0, 3: 1, 15: 2, 20: 3, 7: 4}. Each row is one build(lo, hi) that makes a node, in the order the calls run:

StepCallpre_idxrootmidLeft rangeRight range
1build(0, 4)031inorder[0..0] = [9]inorder[2..4] = [15, 20, 7]
2build(0, 0)190emptyempty
3build(2, 4)2203inorder[2..2] = [15]inorder[4..4] = [7]
4build(2, 2)3152emptyempty
5build(4, 4)474emptyempty
End5tree [3,9,20,null,null,15,7]

Every empty range returns None at if lo > hi without using a preorder value. At step 3, pre_idx is 2 because step 2, the one-node left subtree, used exactly one value. Had build(2, 4) run before build(0, 0), it would have taken preorder[1] = 9 as the right subtree's root, and 9 is not even in inorder[2..4].

Trace Inputpreorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Expected[3,9,20,null,null,15,7]

Example 1, preorder = [3,9,20,15,7], inorder = [9,3,15,20,7], so where = {9: 0, 3: 1, 15: 2, 20: 3, 7: 4}. Each row is one build(lo, hi) that makes a node, in the order the calls run:

StepCallpre_idxrootmidLeft rangeRight range
1build(0, 4)031inorder[0..0] = [9]inorder[2..4] = [15, 20, 7]
2build(0, 0)190emptyempty
3build(2, 4)2203inorder[2..2] = [15]inorder[4..4] = [7]
4build(2, 2)3152emptyempty
5build(4, 4)474emptyempty
End5tree [3,9,20,null,null,15,7]

Every empty range returns None at if lo > hi without using a preorder value. At step 3, pre_idx is 2 because step 2, the one-node left subtree, used exactly one value. Had build(2, 4) run before build(0, 0), it would have taken preorder[1] = 9 as the right subtree's root, and 9 is not even in inorder[2..4].

Code Shape
Archetype Code Shape
Python
1Preorder names the roots and inorder splits them: `preorder[pre_idx]` is the root of the subtree you build next, and its place in `inorder` separates its left subtree from its right one.
2`build(lo, hi)` builds the values in `inorder[lo..hi]`: take `root = TreeNode(preorder[pre_idx])`, move `pre_idx` on, then `mid = where[root.val]` splits the range. `where` maps each value to its inorder index, built once.
3The shape: `if lo > hi: return None`; make `root`; `pre_idx += 1`; `mid = where[root.val]`; `root.left = build(lo, mid - 1)`; `root.right = build(mid + 1, hi)`; `return root`. Start with `build(0, len(inorder) - 1)`.
4The trap: `root.left = build(lo, mid - 1)` must run before `root.right = build(mid + 1, hi)`. Preorder lists the whole left subtree first, so on `preorder = [3,9,20,15,7]` building the right side first gives 9 to the right subtree.

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

One traversal can't pin down a tree: [1, 2] in preorder is 1 with a left child 2 or 1 with a right child 2. Two traversals can, when the values are distinct, because they carry different facts. Preorder names the roots: every subtree starts with its root. Inorder gives the split: a root sits between everything in its left subtree and everything in its right subtree. So take the next root from preorder, find it in inorder, and build each side from its own part of the range. That is Construct Binary Tree: Bottom-Up DFS that builds instead of measuring, with a hash map for the lookup.

🏢 The Analogy: An Org Chart From Two Lists

You get two lists of the same company's staff. The first is a phone tree: every manager is called before anyone in their team, and the first half of each team is called before the second half. The second is a seating plan: each manager sits between the first half of their team and the second half. The next name not yet used on the phone tree is the manager of the group you are placing. Find that name on the seating plan, and the people to the left of it are one half of the team and those to the right the other.

🔑 Why It Works

build(lo, hi) uses exactly one preorder value for every node in inorder[lo..hi], so when the left call returns, pre_idx has just moved past the whole left subtree and points at the right subtree's root. The trap sits on the two calls: the left one must come first, because preorder lists the left subtree before the right one.

  • Building the right subtree before the left one

  • Passing pre_idx down as an argument

  • Calling inorder.index(root.val) in every call

  • Writing lo >= hi as the empty test

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"values are all different", "walked twice" (once into preorder, once into inorder) and "rebuild that tree": two traversal orders of one tree plus distinct values is the signal for Construct Binary Tree. Preorder will name each root, and the distinct values let inorder split around it at one exact index.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

build(lo, hi) builds exactly the values in inorder[lo..hi], and when it starts, preorder[pre_idx] is the root of that range. It makes root = TreeNode(preorder[pre_idx]), moves pre_idx on, finds mid = where[root.val], and builds inorder[lo..mid-1] as the left subtree and inorder[mid+1..hi] as the right one. The answer is build(0, len(inorder) - 1).

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • The card's trap: build the left range before the right one, because pre_idx hands out roots in preorder.

  • Don't slice the lists (buildTree(preorder[1:mid + 1], inorder[:mid])): it gives the right tree, but every call copies its whole range, O(N^2) on a chain of 3000 nodes. Index ranges and one shared pre_idx copy nothing.

The 60-Second Interview Pitch

Say this out loud before you type a single line

I'd use Construct Binary Tree. Preorder always lists a root before the rest of its subtree, so the next unused preorder value is the root of whatever I'm building. Inorder puts that root between its left and right subtrees, so its index there splits the range. I build a map from each value to its inorder index once, keep one shared cursor, pre_idx, into preorder, and write build(lo, hi) for the values of inorder from lo to hi. An empty range gives None; otherwise I take the next preorder value as the root, look up its index, and build the left range, then the right range. The trap is that order: preorder lists the whole left subtree before the right one, so building the right side first would steal the left subtree's root. Each node is created once with an O(1) lookup, so it's O(N) time, with O(N) for the map and O(H) for the recursion stack.

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N)

Building where walks inorder once: N steps. Then every call of build either returns None at once or creates one node, and each node is created exactly once because pre_idx only moves forward. A tree of N nodes has N + 1 empty child slots, so there are 2N + 1 calls. A call that creates a node does O(1) work: one TreeNode, one pre_idx += 1, one dictionary lookup where[root.val], two calls. Total: N + (2N + 1) · O(1) = O(N).

SPACE COMPLEXITY

O(N) index map + O(H) recursion stack

where holds one entry per value: O(N). The recursion goes one level deeper per tree level, so the call stack holds at most H + 1 frames, where H is the tree's height: about log N when balanced, N on a chain. The returned tree has N nodes; that is the output, counted apart from the extra space.

Formal Recurrence Relation

T(N)=N+(2N+1)⋅O(1)=O(N)T(N) = N + (2N + 1) \cdot O(1) = O(N)T(N)=N+(2N+1)⋅O(1)=O(N)

Derivation Progression

Index map

N

where = {val: i for i, val in enumerate(inorder)} visits each value once.

Calls

N + (N + 1) = 2N + 1

One call per node (it creates the node) and one per empty child slot (if lo > hi: return None).

Work per call

O(1)

TreeNode(preorder[pre_idx]), pre_idx += 1 and mid = where[root.val]: no scan of the range.

Total

O(N)

N for the map plus 2N + 1 calls of O(1) each.

Variable Definitions

NNN

Number of nodes: the length of preorder and of inorder

HHH

Height of the tree: about log N when balanced, up to N on a chain

Memory Architecture & Bounds

🟣 Call Stack

O(H): one frame per level of the current root-to-node path

🔵 Auxiliary Heap

O(N): where has one entry per value

🟢 Output Space

O(N): the N nodes of the returned tree (the output, not extra space)

Boundary Best / Worst Cases

Best Case

O(N)O(N)O(N) time and an O(log⁡N)O(\log N)O(logN) stack on a balanced tree

Average Case

O(N)O(N)O(N) time; the stack depth is the tree height

Worst Case

O(N)O(N)O(N) time and an O(N)O(N)O(N) stack on a chain

Graph & Tree Traversal Frontier

Graph & Tree Traversal Frontier
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "walked twice", "values are all different", "rebuild that tree". Two traversal orders of one tree: preorder names each root, and distinct values let inorder split around it at one exact index: Construct Binary Tree.

CONSTRAINTS & BOUNDS

1≤N≤30001 \le N \le 30001≤N≤3000 distinct values in [−3000,3000][-3000, 3000][−3000,3000]. Searching inorder for every root costs up to NNN steps each, about 4.5×1064.5 \times 10^64.5×106 on a chain; a value-to-index map makes each split O(1)O(1)O(1), so the build is O(N)O(N)O(N) time with an O(N)O(N)O(N) map and an O(H)O(H)O(H) recursion stack.

FAANG PRODUCTION TRAPS & EDGE CASES

A chain of 3000 nodes is 3000 calls deep, past the default recursion limit of many runtimes (CPython's is 1000): raise the limit, or build with an explicit stack that pushes each preorder value and pops while its top equals the next inorder value. With repeated values, where keeps one index per value and the split becomes ambiguous: two traversals no longer fix the tree, which is why real serializers write a marker for every missing child or give each node an id.

Theory Context•Depth-First Search (DFS)
MediumLC 105

Construct Binary Tree from Preorder and Inorder Traversal (LeetCode 105)

Target Frequency:AmazonMicrosoftBloomberg

A binary tree whose values are all different was walked twice, and each walk wrote its values into a list.

  • preorder lists every subtree as its root first, then its left subtree, then its right subtree.
  • inorder lists every subtree as its left subtree first, then its root, then its right subtree.

Both lists describe the same tree and hold the same values. Rebuild that tree and return its root.

Worked Examples

Example 1
Input:preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output:[3,9,20,null,null,15,7]
9315207
Explanation: 3 comes first in preorder, so it is the root. In inorder, only 9 sits left of 3, so 9 is the whole left subtree; 15, 20 and 7 sit right of it. Of those, 20 comes first in preorder, so it roots the right side, with 15 on its left and 7 on its right.
Example 2
Input:preorder = [-1], inorder = [-1]
Output:[-1]
-1
Explanation: One value makes a tree of one node.

⚖️Formal Constraints & Bounds

  • 1 <= preorder.length <= 3000

  • inorder.length == preorder.length

  • -3000 <= preorder[i], inorder[i] <= 3000

  • The values in preorder are distinct, and so are the values in inorder.

  • Every value of inorder also appears in preorder.

  • preorder is the tree's preorder traversal and inorder is the same tree's inorder traversal.

Deep-Dive & Conceptual Insights

Core Insight

Preorder gives each subtree's root before anything else in it, and inorder puts that root between its two subtrees. So the next unused preorder value is the root, its inorder index splits the range, and building the left side first keeps the preorder cursor on the right root for the right side.

Real-World Scenario & Production Applications

Tree-shaped data is often stored as a flat list and read back later: a listing of folders written parent-first is a preorder, and so is the order in which many serializers write an object tree. One order alone can't give back the shape; a second order, or a marker for every missing child, can. Restoring a saved tree from such listings is this problem.

Step-by-Step Execution Trace Table

Example 1, preorder = [3,9,20,15,7], inorder = [9,3,15,20,7], so where = {9: 0, 3: 1, 15: 2, 20: 3, 7: 4}. Each row is one build(lo, hi) that makes a node, in the order the calls run:

StepCallpre_idxrootmidLeft rangeRight range
1build(0, 4)031inorder[0..0] = [9]inorder[2..4] = [15, 20, 7]
2build(0, 0)190emptyempty
3build(2, 4)2203inorder[2..2] = [15]inorder[4..4] = [7]
4build(2, 2)3152emptyempty
5build(4, 4)474emptyempty
End5tree [3,9,20,null,null,15,7]

Every empty range returns None at if lo > hi without using a preorder value. At step 3, pre_idx is 2 because step 2, the one-node left subtree, used exactly one value. Had build(2, 4) run before build(0, 0), it would have taken preorder[1] = 9 as the right subtree's root, and 9 is not even in inorder[2..4].

Trace Inputpreorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Expected[3,9,20,null,null,15,7]

Example 1, preorder = [3,9,20,15,7], inorder = [9,3,15,20,7], so where = {9: 0, 3: 1, 15: 2, 20: 3, 7: 4}. Each row is one build(lo, hi) that makes a node, in the order the calls run:

StepCallpre_idxrootmidLeft rangeRight range
1build(0, 4)031inorder[0..0] = [9]inorder[2..4] = [15, 20, 7]
2build(0, 0)190emptyempty
3build(2, 4)2203inorder[2..2] = [15]inorder[4..4] = [7]
4build(2, 2)3152emptyempty
5build(4, 4)474emptyempty
End5tree [3,9,20,null,null,15,7]

Every empty range returns None at if lo > hi without using a preorder value. At step 3, pre_idx is 2 because step 2, the one-node left subtree, used exactly one value. Had build(2, 4) run before build(0, 0), it would have taken preorder[1] = 9 as the right subtree's root, and 9 is not even in inorder[2..4].

Code Shape
Archetype Code Shape
Python
1Preorder names the roots and inorder splits them: `preorder[pre_idx]` is the root of the subtree you build next, and its place in `inorder` separates its left subtree from its right one.
2`build(lo, hi)` builds the values in `inorder[lo..hi]`: take `root = TreeNode(preorder[pre_idx])`, move `pre_idx` on, then `mid = where[root.val]` splits the range. `where` maps each value to its inorder index, built once.
3The shape: `if lo > hi: return None`; make `root`; `pre_idx += 1`; `mid = where[root.val]`; `root.left = build(lo, mid - 1)`; `root.right = build(mid + 1, hi)`; `return root`. Start with `build(0, len(inorder) - 1)`.
4The trap: `root.left = build(lo, mid - 1)` must run before `root.right = build(mid + 1, hi)`. Preorder lists the whole left subtree first, so on `preorder = [3,9,20,15,7]` building the right side first gives 9 to the right subtree.

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

One traversal can't pin down a tree: [1, 2] in preorder is 1 with a left child 2 or 1 with a right child 2. Two traversals can, when the values are distinct, because they carry different facts. Preorder names the roots: every subtree starts with its root. Inorder gives the split: a root sits between everything in its left subtree and everything in its right subtree. So take the next root from preorder, find it in inorder, and build each side from its own part of the range. That is Construct Binary Tree: Bottom-Up DFS that builds instead of measuring, with a hash map for the lookup.

🏢 The Analogy: An Org Chart From Two Lists

You get two lists of the same company's staff. The first is a phone tree: every manager is called before anyone in their team, and the first half of each team is called before the second half. The second is a seating plan: each manager sits between the first half of their team and the second half. The next name not yet used on the phone tree is the manager of the group you are placing. Find that name on the seating plan, and the people to the left of it are one half of the team and those to the right the other.

🔑 Why It Works

build(lo, hi) uses exactly one preorder value for every node in inorder[lo..hi], so when the left call returns, pre_idx has just moved past the whole left subtree and points at the right subtree's root. The trap sits on the two calls: the left one must come first, because preorder lists the left subtree before the right one.

  • Building the right subtree before the left one

  • Passing pre_idx down as an argument

  • Calling inorder.index(root.val) in every call

  • Writing lo >= hi as the empty test

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"values are all different", "walked twice" (once into preorder, once into inorder) and "rebuild that tree": two traversal orders of one tree plus distinct values is the signal for Construct Binary Tree. Preorder will name each root, and the distinct values let inorder split around it at one exact index.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

build(lo, hi) builds exactly the values in inorder[lo..hi], and when it starts, preorder[pre_idx] is the root of that range. It makes root = TreeNode(preorder[pre_idx]), moves pre_idx on, finds mid = where[root.val], and builds inorder[lo..mid-1] as the left subtree and inorder[mid+1..hi] as the right one. The answer is build(0, len(inorder) - 1).

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • The card's trap: build the left range before the right one, because pre_idx hands out roots in preorder.

  • Don't slice the lists (buildTree(preorder[1:mid + 1], inorder[:mid])): it gives the right tree, but every call copies its whole range, O(N^2) on a chain of 3000 nodes. Index ranges and one shared pre_idx copy nothing.

The 60-Second Interview Pitch

Say this out loud before you type a single line

I'd use Construct Binary Tree. Preorder always lists a root before the rest of its subtree, so the next unused preorder value is the root of whatever I'm building. Inorder puts that root between its left and right subtrees, so its index there splits the range. I build a map from each value to its inorder index once, keep one shared cursor, pre_idx, into preorder, and write build(lo, hi) for the values of inorder from lo to hi. An empty range gives None; otherwise I take the next preorder value as the root, look up its index, and build the left range, then the right range. The trap is that order: preorder lists the whole left subtree before the right one, so building the right side first would steal the left subtree's root. Each node is created once with an O(1) lookup, so it's O(N) time, with O(N) for the map and O(H) for the recursion stack.

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N)

Building where walks inorder once: N steps. Then every call of build either returns None at once or creates one node, and each node is created exactly once because pre_idx only moves forward. A tree of N nodes has N + 1 empty child slots, so there are 2N + 1 calls. A call that creates a node does O(1) work: one TreeNode, one pre_idx += 1, one dictionary lookup where[root.val], two calls. Total: N + (2N + 1) · O(1) = O(N).

SPACE COMPLEXITY

O(N) index map + O(H) recursion stack

where holds one entry per value: O(N). The recursion goes one level deeper per tree level, so the call stack holds at most H + 1 frames, where H is the tree's height: about log N when balanced, N on a chain. The returned tree has N nodes; that is the output, counted apart from the extra space.

Formal Recurrence Relation

T(N)=N+(2N+1)⋅O(1)=O(N)T(N) = N + (2N + 1) \cdot O(1) = O(N)T(N)=N+(2N+1)⋅O(1)=O(N)

Derivation Progression

Index map

N

where = {val: i for i, val in enumerate(inorder)} visits each value once.

Calls

N + (N + 1) = 2N + 1

One call per node (it creates the node) and one per empty child slot (if lo > hi: return None).

Work per call

O(1)

TreeNode(preorder[pre_idx]), pre_idx += 1 and mid = where[root.val]: no scan of the range.

Total

O(N)

N for the map plus 2N + 1 calls of O(1) each.

Variable Definitions

NNN

Number of nodes: the length of preorder and of inorder

HHH

Height of the tree: about log N when balanced, up to N on a chain

Memory Architecture & Bounds

🟣 Call Stack

O(H): one frame per level of the current root-to-node path

🔵 Auxiliary Heap

O(N): where has one entry per value

🟢 Output Space

O(N): the N nodes of the returned tree (the output, not extra space)

Boundary Best / Worst Cases

Best Case

O(N)O(N)O(N) time and an O(log⁡N)O(\log N)O(logN) stack on a balanced tree

Average Case

O(N)O(N)O(N) time; the stack depth is the tree height

Worst Case

O(N)O(N)O(N) time and an O(N)O(N)O(N) stack on a chain

Graph & Tree Traversal Frontier

Graph & Tree Traversal Frontier
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "walked twice", "values are all different", "rebuild that tree". Two traversal orders of one tree: preorder names each root, and distinct values let inorder split around it at one exact index: Construct Binary Tree.

CONSTRAINTS & BOUNDS

1≤N≤30001 \le N \le 30001≤N≤3000 distinct values in [−3000,3000][-3000, 3000][−3000,3000]. Searching inorder for every root costs up to NNN steps each, about 4.5×1064.5 \times 10^64.5×106 on a chain; a value-to-index map makes each split O(1)O(1)O(1), so the build is O(N)O(N)O(N) time with an O(N)O(N)O(N) map and an O(H)O(H)O(H) recursion stack.

FAANG PRODUCTION TRAPS & EDGE CASES

A chain of 3000 nodes is 3000 calls deep, past the default recursion limit of many runtimes (CPython's is 1000): raise the limit, or build with an explicit stack that pushes each preorder value and pops while its top equals the next inorder value. With repeated values, where keeps one index per value and the split becomes ambiguous: two traversals no longer fix the tree, which is why real serializers write a marker for every missing child or give each node an id.

© 2026 Hi👋WebEnterprise. All rights reserved.
Sitemap•llms.txt•