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)
HardLC 834

Sum of Distances in Tree (LeetCode 834)

A tree has n nodes numbered 0 to n - 1, joined by n - 1 undirected edges. The edges list holds the edges: [a, b] means that node a and node b are joined. The distance between two nodes is the number of edges on the one path that joins them.

For every node i, add up the distances from node i to all other nodes. Return these n sums as a list answer, where answer[i] is the sum for node i.

The sums are independent: each node is a separate question, as if that node were the root of the tree.

Worked Examples

Example 1
Input:n = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]
Output:[8,12,6,10,10,10]
012345
Explanation: Node 0 is 1 step from node 1, 1 step from node 2, and 2 steps from each of nodes 3, 4 and 5, so answer[0] = 1 + 1 + 2 + 2 + 2 = 8. Node 1 is 1 step from node 0 and 2 steps from node 2, then 3 steps from each of nodes 3, 4 and 5, so answer[1] = 12. The other sums follow the same way.
Example 2
Input:n = 1, edges = []
Output:[0]
0
Explanation: A tree with one node has no other node, so the only sum is 0.
Example 3
Input:n = 2, edges = [[1,0]]
Output:[1,1]
10
Explanation: Each node is 1 step from the other node, so both sums are 1.

⚖️Formal Constraints & Bounds

  • 1 <= n <= 3 * 104

  • edges.length == n - 1

  • edges[i].length == 2

  • 0 <= ai, bi < n

  • ai != bi

  • The given input represents a valid tree.

Deep-Dive & Conceptual Insights

Core Insight

Moving the root across one edge changes the distance of every node by exactly one step: the nodes on the new root's side get one step closer, and the nodes on the other side get one step farther. So answer[node] follows from answer[parent[node]] with count[node] and down[node], and no node needs a new walk of the whole tree.

Real-World Scenario & Production Applications

A company wants to place a warehouse in one of its cities. The cities are joined by roads that form a tree, and every other city is served from the warehouse. The best city is the one with the smallest total distance to all the others. To compare the cities, you need this total for every city, not for one city.

Step-by-Step Execution Trace Table

Example 1, n = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]. Walking from node 0 gives order = [0, 1, 2, 3, 4, 5] and parent = [-1, 0, 0, 2, 2, 2].

First pass (for node in reversed(order)): each node adds itself to its parent's count and its subtree's distances to its parent's down.

Stepnodecount[parent] afterdown[parent] afterWhat it means
15count[2] = 2down[2] = 1Node 5 is 1 step below node 2
24count[2] = 3down[2] = 2Node 4 is 1 step below node 2
33count[2] = 4down[2] = 3Node 3 is 1 step below node 2
42count[0] = 5down[0] = 7The 4 nodes of the subtree of node 2 are 3 + 4 = 7 steps below node 0
51count[0] = 6down[0] = 8Node 1 adds 1 step, so answer[0] = down[0] = 8

Second pass (for node in order[1:]): outside_count = n - count[node], outside_sum = answer[parent[node]] - down[node] - count[node] and answer[node] = down[node] + outside_sum + outside_count.

Stepnodeoutside_countoutside_sumanswer[node]What it means
6157120 + 7 + 5: the 5 nodes outside are each 1 step farther than from node 0
722163 + 1 + 2: the subtree part is 3, the outside part is node 0 and node 1
8355100 + 5 + 5, with outside_sum = 6 - 0 - 1
945510Same as node 3
1055510Same as node 3

The result is [8, 12, 6, 10, 10, 10]. Leaving out outside_count would give [8, 7, 4, 3, 3, 3].

Trace Inputn = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]
Expected[8,12,6,10,10,10]

Example 1, n = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]. Walking from node 0 gives order = [0, 1, 2, 3, 4, 5] and parent = [-1, 0, 0, 2, 2, 2].

First pass (for node in reversed(order)): each node adds itself to its parent's count and its subtree's distances to its parent's down.

Stepnodecount[parent] afterdown[parent] afterWhat it means
15count[2] = 2down[2] = 1Node 5 is 1 step below node 2
24count[2] = 3down[2] = 2Node 4 is 1 step below node 2
33count[2] = 4down[2] = 3Node 3 is 1 step below node 2
42count[0] = 5down[0] = 7The 4 nodes of the subtree of node 2 are 3 + 4 = 7 steps below node 0
51count[0] = 6down[0] = 8Node 1 adds 1 step, so answer[0] = down[0] = 8

Second pass (for node in order[1:]): outside_count = n - count[node], outside_sum = answer[parent[node]] - down[node] - count[node] and answer[node] = down[node] + outside_sum + outside_count.

Stepnodeoutside_countoutside_sumanswer[node]What it means
6157120 + 7 + 5: the 5 nodes outside are each 1 step farther than from node 0
722163 + 1 + 2: the subtree part is 3, the outside part is node 0 and node 1
8355100 + 5 + 5, with outside_sum = 6 - 0 - 1
945510Same as node 3
1055510Same as node 3

The result is [8, 12, 6, 10, 10, 10]. Leaving out outside_count would give [8, 7, 4, 3, 3, 3].

Code Shape
Archetype Code Shape
Python
1Solve the tree once with node 0 as the root, then change the answer by a small amount across each edge instead of starting a new walk from every node.
2First pass, `for node in reversed(order)`: `count[parent[node]] += count[node]` and `down[parent[node]] += down[node] + count[node]`.
3Second pass, `for node in order[1:]`: `outside_count = n - count[node]`, `outside_sum = answer[parent[node]] - down[node] - count[node]`, then `answer[node] = down[node] + outside_sum + outside_count`.
4The trap: `outside_count` is part of the answer. Without it, node 1 of `n = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]` gets `7` instead of `12`.

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Some problems ask for one answer for every node, as if that node were the root of the tree. The sum of the distances from a node to all other nodes is one example. The slow way starts a new walk of the whole tree from every node. That is N walks over N nodes each.

Two neighbouring roots give almost the same tree. When the root moves across one edge, only the nodes on the two sides of that edge change their distance, and every one of them changes by exactly one step. So the tree is solved once, with node 0 as the root, and each other node gets its answer by a small change to the answer of its parent.

📚 The Analogy: A Company With A Head Office

A company has offices joined in a tree, and one office is the head office. Every office reports two numbers to its manager: how many offices work below it, and how far away they are in total. That is the first pass, from the bottom up.

Now an office wants the same numbers as if it were the head office. It already knows its own part, the offices below it. Its manager knows everything else. So the manager hands down the numbers for the rest of the company, and the office adds one step for every office in that rest. That is the second pass, from the top down.

🔑 Why It Works

The first pass is a walk from the leaves up, the same walk as in Bottom-Up DFS. count[node] is the number of nodes in the subtree of node, and down[node] is the sum of their distances from node. Each parent adds the pieces of its children, and each node of a child's subtree is one step farther from the parent, so down[parent[node]] += down[node] + count[node].

The second pass is a walk from the root down. The root has nothing outside its subtree, so answer[0] = down[0]. For a child, answer[parent[node]] is already final. The nodes outside the subtree of node number outside_count = n - count[node]. Their distances from the parent are outside_sum = answer[parent[node]] - down[node] - count[node]. From node each of them is one step farther, so answer[node] = down[node] + outside_sum + outside_count.

Every node is handled once in each pass, so the work is O(N) and no node starts a new walk of the whole tree.

  • Leaving out outside_count when the root moves to node

  • Taking only down[node] out of the parent's total

  • Running the second pass over reversed(order[1:])

  • Adding down[node] without count[node] in the first pass

  • Walking both passes with recursion on a long path

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"For each node i, report the total of its distances to every other node" asks for one answer per node, each as if that node were the root. A walk from every node costs O(N^2), and N can be 30,000. The answers of two neighbouring nodes differ by a small amount, so the tree is solved once and the answer is moved across each edge. This is Rerooting.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

After the first pass, count[node] is the size of the subtree of node and down[node] is the sum of the distances from node to the nodes in it. When the second pass reaches node, answer[parent[node]] is final. The outside_count = n - count[node] nodes outside the subtree of node are at outside_sum in total from the parent, and each is one step farther from node. So answer[node] = down[node] + outside_sum + outside_count.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • The card's trap: answer[node] = down[node] + outside_sum + outside_count. Without outside_count, the six-node example gives [8, 7, 4, 3, 3, 3] instead of [8, 12, 6, 10, 10, 10].

  • outside_sum = answer[parent[node]] - down[node] - count[node]: taking out only down[node] leaves count[node] extra steps in the outside part, and node 1 of the six-node example gets 13 instead of 12.

  • for node in order[1:], never reversed(order[1:]): a child needs the finished answer of its parent, and in reverse order that answer is still 0.

  • n = 1, edges = []: order = [0], the second pass has no node to handle, and answer = [down[0]] = [0]. Code that reads edges[0] fails on this input.

  • A path of 30,000 nodes is a valid input. A recursive walk goes 30,000 calls deep and raises RecursionError in Python, so order is built with a loop.

The 60-Second Interview Pitch

Say this out loud before you type a single line

I'd solve this with Rerooting. A walk from every node costs N times N, which is too slow for 30,000 nodes. So I root the tree at node 0 and order the nodes so that every parent comes before its children. In the first pass, from the leaves up, each node learns the size of its subtree and the sum of its distances to the nodes below it. Every node of a child's subtree is one step farther from the parent, so the parent adds the child's sum plus the child's size. Node 0 now has its final answer. In the second pass, from the root down, each child takes its parent's answer, removes its own subtree, and adds one step for every node outside it. That gives each node its answer as if it were the root. Each node is handled twice, so the time is O(N) and the space is O(N).

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N)

Look at the code: the loop over order scans each node's neighbors once, so building order visits each of the N nodes and each of the N - 1 edges (twice, once from each end). The first pass handles each node once with O(1) work. The second pass handles each node once with O(1) work. Three loops of O(N) work each give O(N).

SPACE COMPLEXITY

O(N)

The lists neighbors, parent, order, count, down and answer each hold N values (neighbors holds 2 * (N - 1) entries in total). No recursion is used, so there is no call stack. The extra space is O(N), and answer is also the output.

Formal Recurrence Relation

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

Derivation Progression

Order the nodes

N + 2E steps

The loop over order visits each node once and looks at each edge from both of its ends.

First pass

N updates

count[parent[node]] += count[node] and down[parent[node]] += down[node] + count[node] run once for each node.

Second pass

N - 1 updates

outside_count, outside_sum and answer[node] are three O(1) formulas for each node except node 0.

Total

O(N)

Three loops of O(N) work each; no node starts a new walk of the tree.

Variable Definitions

NNN

Number of nodes in the tree (up to 30,000 here)

EEE

Number of edges, always N - 1 in a tree

Memory Architecture & Bounds

🟣 Call Stack

O(1): iterative, no recursion

🔵 Auxiliary Heap

O(N): neighbors, parent, order, count and down

🟢 Output Space

O(N): the returned list answer

Boundary Best / Worst Cases

Best Case

O(N)O(N)O(N): both passes visit every node, whatever the shape of the tree

Average Case

O(N)O(N)O(N) time and space

Worst Case

O(N)O(N)O(N) time and space, for a path or a star as well

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: "for each node i, add up its distances to every other node", "one result per node, in a list of length n". One answer for every node, each as if that node were the root: Rerooting, which solves the tree once and then moves the answer across each edge.

CONSTRAINTS & BOUNDS

n≤3⋅104n \le 3 \cdot 10^4n≤3⋅104. A walk from every node costs O(n2)O(n^2)O(n2), about 9⋅1089 \cdot 10^89⋅108 steps at the limit. Rerooting costs O(n)O(n)O(n) time and O(n)O(n)O(n) memory. The largest sum is about n2/2n^2 / 2n2/2, which is 449,985,000449{,}985{,}000449,985,000 on a path of 3⋅1043 \cdot 10^43⋅104 nodes, so a 32-bit integer is enough.

FAANG PRODUCTION TRAPS & EDGE CASES

A path of 3⋅1043 \cdot 10^43⋅104 nodes is 3⋅1043 \cdot 10^43⋅104 levels deep: a recursive walk can overflow a small call stack, so order is built with a loop. The two passes read a tree that must not change between them: if an edge is added or removed after the first pass, count and down are out of date.

Theory Context•Depth-First Search (DFS)
HardLC 834

Sum of Distances in Tree (LeetCode 834)

A tree has n nodes numbered 0 to n - 1, joined by n - 1 undirected edges. The edges list holds the edges: [a, b] means that node a and node b are joined. The distance between two nodes is the number of edges on the one path that joins them.

For every node i, add up the distances from node i to all other nodes. Return these n sums as a list answer, where answer[i] is the sum for node i.

The sums are independent: each node is a separate question, as if that node were the root of the tree.

Worked Examples

Example 1
Input:n = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]
Output:[8,12,6,10,10,10]
012345
Explanation: Node 0 is 1 step from node 1, 1 step from node 2, and 2 steps from each of nodes 3, 4 and 5, so answer[0] = 1 + 1 + 2 + 2 + 2 = 8. Node 1 is 1 step from node 0 and 2 steps from node 2, then 3 steps from each of nodes 3, 4 and 5, so answer[1] = 12. The other sums follow the same way.
Example 2
Input:n = 1, edges = []
Output:[0]
0
Explanation: A tree with one node has no other node, so the only sum is 0.
Example 3
Input:n = 2, edges = [[1,0]]
Output:[1,1]
10
Explanation: Each node is 1 step from the other node, so both sums are 1.

⚖️Formal Constraints & Bounds

  • 1 <= n <= 3 * 104

  • edges.length == n - 1

  • edges[i].length == 2

  • 0 <= ai, bi < n

  • ai != bi

  • The given input represents a valid tree.

Deep-Dive & Conceptual Insights

Core Insight

Moving the root across one edge changes the distance of every node by exactly one step: the nodes on the new root's side get one step closer, and the nodes on the other side get one step farther. So answer[node] follows from answer[parent[node]] with count[node] and down[node], and no node needs a new walk of the whole tree.

Real-World Scenario & Production Applications

A company wants to place a warehouse in one of its cities. The cities are joined by roads that form a tree, and every other city is served from the warehouse. The best city is the one with the smallest total distance to all the others. To compare the cities, you need this total for every city, not for one city.

Step-by-Step Execution Trace Table

Example 1, n = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]. Walking from node 0 gives order = [0, 1, 2, 3, 4, 5] and parent = [-1, 0, 0, 2, 2, 2].

First pass (for node in reversed(order)): each node adds itself to its parent's count and its subtree's distances to its parent's down.

Stepnodecount[parent] afterdown[parent] afterWhat it means
15count[2] = 2down[2] = 1Node 5 is 1 step below node 2
24count[2] = 3down[2] = 2Node 4 is 1 step below node 2
33count[2] = 4down[2] = 3Node 3 is 1 step below node 2
42count[0] = 5down[0] = 7The 4 nodes of the subtree of node 2 are 3 + 4 = 7 steps below node 0
51count[0] = 6down[0] = 8Node 1 adds 1 step, so answer[0] = down[0] = 8

Second pass (for node in order[1:]): outside_count = n - count[node], outside_sum = answer[parent[node]] - down[node] - count[node] and answer[node] = down[node] + outside_sum + outside_count.

Stepnodeoutside_countoutside_sumanswer[node]What it means
6157120 + 7 + 5: the 5 nodes outside are each 1 step farther than from node 0
722163 + 1 + 2: the subtree part is 3, the outside part is node 0 and node 1
8355100 + 5 + 5, with outside_sum = 6 - 0 - 1
945510Same as node 3
1055510Same as node 3

The result is [8, 12, 6, 10, 10, 10]. Leaving out outside_count would give [8, 7, 4, 3, 3, 3].

Trace Inputn = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]
Expected[8,12,6,10,10,10]

Example 1, n = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]. Walking from node 0 gives order = [0, 1, 2, 3, 4, 5] and parent = [-1, 0, 0, 2, 2, 2].

First pass (for node in reversed(order)): each node adds itself to its parent's count and its subtree's distances to its parent's down.

Stepnodecount[parent] afterdown[parent] afterWhat it means
15count[2] = 2down[2] = 1Node 5 is 1 step below node 2
24count[2] = 3down[2] = 2Node 4 is 1 step below node 2
33count[2] = 4down[2] = 3Node 3 is 1 step below node 2
42count[0] = 5down[0] = 7The 4 nodes of the subtree of node 2 are 3 + 4 = 7 steps below node 0
51count[0] = 6down[0] = 8Node 1 adds 1 step, so answer[0] = down[0] = 8

Second pass (for node in order[1:]): outside_count = n - count[node], outside_sum = answer[parent[node]] - down[node] - count[node] and answer[node] = down[node] + outside_sum + outside_count.

Stepnodeoutside_countoutside_sumanswer[node]What it means
6157120 + 7 + 5: the 5 nodes outside are each 1 step farther than from node 0
722163 + 1 + 2: the subtree part is 3, the outside part is node 0 and node 1
8355100 + 5 + 5, with outside_sum = 6 - 0 - 1
945510Same as node 3
1055510Same as node 3

The result is [8, 12, 6, 10, 10, 10]. Leaving out outside_count would give [8, 7, 4, 3, 3, 3].

Code Shape
Archetype Code Shape
Python
1Solve the tree once with node 0 as the root, then change the answer by a small amount across each edge instead of starting a new walk from every node.
2First pass, `for node in reversed(order)`: `count[parent[node]] += count[node]` and `down[parent[node]] += down[node] + count[node]`.
3Second pass, `for node in order[1:]`: `outside_count = n - count[node]`, `outside_sum = answer[parent[node]] - down[node] - count[node]`, then `answer[node] = down[node] + outside_sum + outside_count`.
4The trap: `outside_count` is part of the answer. Without it, node 1 of `n = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]` gets `7` instead of `12`.

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Some problems ask for one answer for every node, as if that node were the root of the tree. The sum of the distances from a node to all other nodes is one example. The slow way starts a new walk of the whole tree from every node. That is N walks over N nodes each.

Two neighbouring roots give almost the same tree. When the root moves across one edge, only the nodes on the two sides of that edge change their distance, and every one of them changes by exactly one step. So the tree is solved once, with node 0 as the root, and each other node gets its answer by a small change to the answer of its parent.

📚 The Analogy: A Company With A Head Office

A company has offices joined in a tree, and one office is the head office. Every office reports two numbers to its manager: how many offices work below it, and how far away they are in total. That is the first pass, from the bottom up.

Now an office wants the same numbers as if it were the head office. It already knows its own part, the offices below it. Its manager knows everything else. So the manager hands down the numbers for the rest of the company, and the office adds one step for every office in that rest. That is the second pass, from the top down.

🔑 Why It Works

The first pass is a walk from the leaves up, the same walk as in Bottom-Up DFS. count[node] is the number of nodes in the subtree of node, and down[node] is the sum of their distances from node. Each parent adds the pieces of its children, and each node of a child's subtree is one step farther from the parent, so down[parent[node]] += down[node] + count[node].

The second pass is a walk from the root down. The root has nothing outside its subtree, so answer[0] = down[0]. For a child, answer[parent[node]] is already final. The nodes outside the subtree of node number outside_count = n - count[node]. Their distances from the parent are outside_sum = answer[parent[node]] - down[node] - count[node]. From node each of them is one step farther, so answer[node] = down[node] + outside_sum + outside_count.

Every node is handled once in each pass, so the work is O(N) and no node starts a new walk of the whole tree.

  • Leaving out outside_count when the root moves to node

  • Taking only down[node] out of the parent's total

  • Running the second pass over reversed(order[1:])

  • Adding down[node] without count[node] in the first pass

  • Walking both passes with recursion on a long path

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"For each node i, report the total of its distances to every other node" asks for one answer per node, each as if that node were the root. A walk from every node costs O(N^2), and N can be 30,000. The answers of two neighbouring nodes differ by a small amount, so the tree is solved once and the answer is moved across each edge. This is Rerooting.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

After the first pass, count[node] is the size of the subtree of node and down[node] is the sum of the distances from node to the nodes in it. When the second pass reaches node, answer[parent[node]] is final. The outside_count = n - count[node] nodes outside the subtree of node are at outside_sum in total from the parent, and each is one step farther from node. So answer[node] = down[node] + outside_sum + outside_count.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • The card's trap: answer[node] = down[node] + outside_sum + outside_count. Without outside_count, the six-node example gives [8, 7, 4, 3, 3, 3] instead of [8, 12, 6, 10, 10, 10].

  • outside_sum = answer[parent[node]] - down[node] - count[node]: taking out only down[node] leaves count[node] extra steps in the outside part, and node 1 of the six-node example gets 13 instead of 12.

  • for node in order[1:], never reversed(order[1:]): a child needs the finished answer of its parent, and in reverse order that answer is still 0.

  • n = 1, edges = []: order = [0], the second pass has no node to handle, and answer = [down[0]] = [0]. Code that reads edges[0] fails on this input.

  • A path of 30,000 nodes is a valid input. A recursive walk goes 30,000 calls deep and raises RecursionError in Python, so order is built with a loop.

The 60-Second Interview Pitch

Say this out loud before you type a single line

I'd solve this with Rerooting. A walk from every node costs N times N, which is too slow for 30,000 nodes. So I root the tree at node 0 and order the nodes so that every parent comes before its children. In the first pass, from the leaves up, each node learns the size of its subtree and the sum of its distances to the nodes below it. Every node of a child's subtree is one step farther from the parent, so the parent adds the child's sum plus the child's size. Node 0 now has its final answer. In the second pass, from the root down, each child takes its parent's answer, removes its own subtree, and adds one step for every node outside it. That gives each node its answer as if it were the root. Each node is handled twice, so the time is O(N) and the space is O(N).

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N)

Look at the code: the loop over order scans each node's neighbors once, so building order visits each of the N nodes and each of the N - 1 edges (twice, once from each end). The first pass handles each node once with O(1) work. The second pass handles each node once with O(1) work. Three loops of O(N) work each give O(N).

SPACE COMPLEXITY

O(N)

The lists neighbors, parent, order, count, down and answer each hold N values (neighbors holds 2 * (N - 1) entries in total). No recursion is used, so there is no call stack. The extra space is O(N), and answer is also the output.

Formal Recurrence Relation

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

Derivation Progression

Order the nodes

N + 2E steps

The loop over order visits each node once and looks at each edge from both of its ends.

First pass

N updates

count[parent[node]] += count[node] and down[parent[node]] += down[node] + count[node] run once for each node.

Second pass

N - 1 updates

outside_count, outside_sum and answer[node] are three O(1) formulas for each node except node 0.

Total

O(N)

Three loops of O(N) work each; no node starts a new walk of the tree.

Variable Definitions

NNN

Number of nodes in the tree (up to 30,000 here)

EEE

Number of edges, always N - 1 in a tree

Memory Architecture & Bounds

🟣 Call Stack

O(1): iterative, no recursion

🔵 Auxiliary Heap

O(N): neighbors, parent, order, count and down

🟢 Output Space

O(N): the returned list answer

Boundary Best / Worst Cases

Best Case

O(N)O(N)O(N): both passes visit every node, whatever the shape of the tree

Average Case

O(N)O(N)O(N) time and space

Worst Case

O(N)O(N)O(N) time and space, for a path or a star as well

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: "for each node i, add up its distances to every other node", "one result per node, in a list of length n". One answer for every node, each as if that node were the root: Rerooting, which solves the tree once and then moves the answer across each edge.

CONSTRAINTS & BOUNDS

n≤3⋅104n \le 3 \cdot 10^4n≤3⋅104. A walk from every node costs O(n2)O(n^2)O(n2), about 9⋅1089 \cdot 10^89⋅108 steps at the limit. Rerooting costs O(n)O(n)O(n) time and O(n)O(n)O(n) memory. The largest sum is about n2/2n^2 / 2n2/2, which is 449,985,000449{,}985{,}000449,985,000 on a path of 3⋅1043 \cdot 10^43⋅104 nodes, so a 32-bit integer is enough.

FAANG PRODUCTION TRAPS & EDGE CASES

A path of 3⋅1043 \cdot 10^43⋅104 nodes is 3⋅1043 \cdot 10^43⋅104 levels deep: a recursive walk can overflow a small call stack, so order is built with a loop. The two passes read a tree that must not change between them: if an edge is added or removed after the first pass, count and down are out of date.

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