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•Breadth-First Search (BFS)
MediumLC 994

Rotting Oranges (LeetCode 994)

Target Frequency:AmazonGoogleMeta

You're given an m x n grid whose cells each hold one of three values: 0 for an empty cell, 1 for a fresh orange, and 2 for a rotten one.

Each minute, rot spreads: any fresh orange touching a rotten orange along one of the four cardinal directions turns rotten too.

Work out how many minutes must pass before every orange is rotten, and return -1 if some fresh orange can never be reached.

Worked Examples

Example 1
Input:grid = [[2,1,1],[1,1,0],[0,1,1]]
Output:4
211110011
Explanation: Rot spreads one step per minute; the orange in the bottom-right corner is the last to turn, at minute 4.
Example 2
Input:grid = [[2,1,1],[0,1,1],[1,0,1]]
Output:-1
Explanation: The orange at row 2, column 0 stays fresh forever, since rot only spreads through direct up/down/left/right contact, not diagonally.
Example 3
Input:grid = [[0,2]]
Output:0
Explanation: There are no fresh oranges to begin with, so the elapsed time is 0.

⚖️Formal Constraints & Bounds

  • m == grid.length

  • n == grid[i].length

  • 1 <= m, n <= 10

  • grid[i][j] is 0, 1, or 2.

Deep-Dive & Conceptual Insights

Core Insight

Marking oranges rotten at enqueue time prevents duplicate queue entries and models parallel infection waves accurately.

Real-World Scenario & Production Applications

Epidemiological virus contagion modeling and wildfire spread perimeter simulations.

Subproblems & Recurrence Decomposition3 Phases

🧩Subproblem 1: Multi-Source Origin Seeding

Scan the entire grid. Enqueue all initial source coordinates (e.g. all rotten oranges, distance 0) and count target elements.

Mathematical Recurrence / Code Invariant
queue = deque()
fresh = 0
for r in range(R):
    for c in range(C):
        if grid[r][c] == 2: queue.append((r, c))
        elif grid[r][c] == 1: fresh += 1

Step-by-Step Execution Trace Table

Code / Blueprint
Minute 0 (Initial Seed): Minute 1 (Infect): Minute 2 (Infect):
[ 2 ] [ 1 ] [ 1 ] [ 2 ] [ 2 ] [ 1 ] [ 2 ] [ 2 ] [ 2 ]
[ 1 ] [ 1 ] [ 0 ] ==> [ 2 ] [ 1 ] [ 0 ] ==> [ 2 ] [ 2 ] [ 0 ]
[ 0 ] [ 1 ] [ 1 ] [ 0 ] [ 1 ] [ 1 ] [ 0 ] [ 1 ] [ 1 ]
queue=[(0,0)], fresh=6 queue=[(0,1),(1,0)], fresh=4 queue=[(0,2),(1,1)], fresh=2
 
Minute 3 (Infect): Minute 4 (Final Fresh):
[ 2 ] [ 2 ] [ 2 ] [ 2 ] [ 2 ] [ 2 ]
[ 2 ] [ 2 ] [ 0 ] ==> [ 2 ] [ 2 ] [ 0 ]
[ 0 ] [ 2 ] [ 1 ] [ 0 ] [ 2 ] [ 2 ]
queue=[(2,1)], fresh=1 queue=[(2,2)], fresh=0 (Goal Reached! ✅)
 
Step-by-Step Wavefront Progression
  1. Step 1 (Multi-Source Seed, Minute 0, d=0): queue = [(0, 0)], fresh = 6. Scan grid at t=0t=0t=0: count 6 fresh oranges, enqueue initial rotten cell (0, 0).
  2. Step 2 (Snapshot Minute 0, level_size=1, d=0): level_size = 1. Minute 0 wavefront starts.
  3. Step 3 (Pop (0,0) & Infect (0,1), (1,0), d=0): Pop (0, 0). 4-directional neighbors: infect (0, 1) and (1, 0). Mark on enqueue: grid[0][1]=2, grid[1][0]=2. fresh = 4. queue = [(0, 1), (1, 0)]. Minute advances: minutes = 1 🌊.
  4. Step 4 (Snapshot Minute 1, level_size=2, d=1): level_size = 2. Minute 1 wavefront. queue = [(0, 1), (1, 0)].
  5. Step 5 (Pop (0,1) & Infect (0,2), (1,1), d=1): Pop (0, 1). Infect (0, 2) and (1, 1). Mark on enqueue →\rightarrow→ fresh = 2. queue = [(1, 0), (0, 2), (1, 1)].
  6. Step 6 (Pop (1,0), Diamond Convergence Guard, d=1): Pop (1, 0). Neighbor (1, 1) is already marked 2 (visited)! Guard prevents duplicate enqueue. Minute advances: minutes = 2 🌊.
  7. Step 7 (Snapshot Minute 2, level_size=2, d=2): level_size = 2. Minute 2 wavefront. queue = [(0, 2), (1, 1)].
  8. Step 8 (Pop (0,2), Blocked Neighbor (1,2)=0, d=2): Pop (0, 2). Neighbor (1, 2) is empty (0) →\rightarrow→ skip.
  9. Step 9 (Pop (1,1) & Infect (2,1), d=2): Pop (1, 1). Infect (2, 1). Mark on enqueue →\rightarrow→ fresh = 1. queue = [(2, 1)]. Minute advances: minutes = 3 🌊.
  10. Step 10 (Snapshot Minute 3, level_size=1, d=3): level_size = 1. Minute 3 wavefront. queue = [(2, 1)].
  11. Step 11 (Pop (2,1) & Infect Final (2,2), d=3): Pop (2, 1). Infect (2, 2). Mark on enqueue →\rightarrow→ fresh = 0. queue = [(2, 2)]. Minute advances: minutes = 4 🌊.
  12. Step 12 (Goal Check, fresh == 0, Complete, d=4): fresh = 0. All oranges fully rotted. Search Complete: return 4 ✅.
Full Walkthrough12 Steps
Input:grid = [[2,1,1],[1,1,0],[0,1,1]]Expected:4
1🌱 ROOT(Multi-Source Seed, Minute 0, d=0)
queue = [(0, 0)]
queue = [(0, 0)], fresh = 6. Scan grid at t=0t=0t=0: count 6 fresh oranges, enqueue initial rotten cell (0, 0).
2🌱 ROOT(Snapshot Minute 0, level_size=1, d=0)
level_size = 1
level_size = 1. Minute 0 wavefront starts.
3🌱 ROOT(Pop (0,0) & Infect (0,1), (1,0), d=0)
queue = [(0, 1), (1, 0)]
Pop (0, 0). 4-directional neighbors: infect (0, 1) and (1, 0). Mark on enqueue: grid[0][1]=2, grid[1][0]=2. fresh = 4. queue = [(0, 1), (1, 0)]. Minute advances: minutes = 1 🌊.
4📸 SNAPSHOT(Snapshot Minute 1, level_size=2, d=1)
queue = [(0, 1), (1, 0)]
level_size = 2. Minute 1 wavefront. queue = [(0, 1), (1, 0)].
5🌊 ENQUEUE / DISCOVER(Pop (0,1) & Infect (0,2), (1,1), d=1)
queue = [(1, 0), (0, 2), (1, 1)]
Pop (0, 1). Infect (0, 2) and (1, 1). Mark on enqueue →\rightarrow→ fresh = 2. queue = [(1, 0), (0, 2), (1, 1)].
6🌊 ENQUEUE / DISCOVER(Pop (1,0), Diamond Convergence Guard, d=1)
Pop (1, 0). Neighbor (1, 1) is already marked 2 (visited)! Guard prevents duplicate enqueue. Minute advances: minutes = 2 🌊.
7📸 SNAPSHOT(Snapshot Minute 2, level_size=2, d=2)
queue = [(0, 2), (1, 1)]
level_size = 2. Minute 2 wavefront. queue = [(0, 2), (1, 1)].
8⚡ FIFO DRAIN(Pop (0,2), Blocked Neighbor (1,2)=0, d=2)
Pop (0, 2). Neighbor (1, 2) is empty (0) →\rightarrow→ skip.
9🌊 ENQUEUE / DISCOVER(Pop (1,1) & Infect (2,1), d=2)
queue = [(2, 1)]
Pop (1, 1). Infect (2, 1). Mark on enqueue →\rightarrow→ fresh = 1. queue = [(2, 1)]. Minute advances: minutes = 3 🌊.
10📸 SNAPSHOT(Snapshot Minute 3, level_size=1, d=3)
queue = [(2, 1)]
level_size = 1. Minute 3 wavefront. queue = [(2, 1)].
11🌊 ENQUEUE / DISCOVER(Pop (2,1) & Infect Final (2,2), d=3)
queue = [(2, 2)]
Pop (2, 1). Infect (2, 2). Mark on enqueue →\rightarrow→ fresh = 0. queue = [(2, 2)]. Minute advances: minutes = 4 🌊.
12✅ RECORD / GOAL(Goal Check, fresh == 0, Complete, d=4)
fresh = 0
fresh = 0. All oranges fully rotted. Search Complete: return 4 ✅.
Code Shape
Archetype Code Shape
Python
1queue = deque(all_rotten_cells)
2fresh = count_fresh()
3minutes = 0
4while queue and fresh > 0:
5 for _ in range(len(queue)):
6 r, c = queue.popleft()
7 make each fresh neighbor in the 4 directions rotten, fresh -= 1, and append it to queue
8 minutes += 1
9return minutes if fresh == 0 else -1

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Every earlier BFS in this pattern started from exactly one node. Here, the infection starts from potentially dozens of places at the same instant — and the fix is almost embarrassingly simple.

🪨 The Analogy: Dropping Several Stones in a Pond at Once

Picture dropping several stones into a pond at the exact same moment, instead of just one. Each stone starts its own ripple, and all the ripples expand outward in perfect sync — where two ripples meet, both arrived at that point during the very same "tick." You don't need to simulate each stone's ripple separately and then merge them; if you drop all the stones into the water before the clock starts, a single shared expanding wavefront naturally emerges.

🔑 The Key Idea in the Code

Seeding the queue with every starting point before the first level even begins is the entire adaptation — the level-by-level snapshot loop doesn't need to change at all; it just now represents "everyone one minute away from any rotten orange," not "everyone one step from a single source."

  • Single-source BFS instead of multi-source wavefront: Running BFS from one rotten orange at a time fails to model concurrent infection. All initial rotten oranges must be enqueued at minute 0 simultaneously.

  • Incrementing timer when no fresh oranges rot: Incrementing the minute counter on every level even when no adjacent fresh oranges were infected overcounts total minutes.

  • Isolated fresh oranges unreached: Forgetting to check if fresh_count > 0 after the queue is empty, returning elapsed minutes when fresh oranges were actually unreachable (must return -1).

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"Each minute, rot spreads" from every rotten orange at once, and the question is "how many minutes must pass before every orange is rotten", or -1. Many sources, one shared clock: Multi-Source BFS.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

After minutes rounds, queue holds exactly the oranges that turned rotten in round minutes, and fresh_count counts the fresh oranges not reached yet. The decision: a fresh neighbour turns rotten (grid[nr][nc] = 2) and is pushed in the round that first reaches it.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • return minutes if fresh_count == 0 else -1: a fresh orange walled off by empty cells is never reached, so the answer is -1, not the minutes counted so far.

  • while queue and fresh_count > 0: the card's trap; without fresh_count > 0 the last round reaches nothing new and adds one minute too many.

  • if fresh_count == 0: return 0: a grid with no fresh orange needs 0 minutes, even when it has no rotten orange either.

The 60-Second Interview Pitch

Say this out loud before you type a single line

This is Multi-Source BFS. Every rotten orange spreads at the same time, so I put all of them in the queue at minute zero and count the fresh ones. Then I run level-by-level BFS: each round is one minute, and in that round every orange in the queue turns its fresh neighbours rotten, which I mark right away and push for the next round. Because all sources grow one ring per round together, each fresh orange rots in the round of its nearest rotten orange, so the round count is the answer. The trap is the last round: I only start a round while fresh oranges remain, otherwise a final round that reaches nothing adds one minute too many. If the queue empties with fresh oranges left, some are walled off, and I return minus one. Each cell is pushed at most once, so it's O(R times C) time and space.

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(R * C)

Look at the code: the double for r in range(R): for c in range(C) scan visits each of the R · C cells once. In while queue and fresh_count > 0, a cell is pushed only when it turns from 1 to 2, so each cell is pushed at most once, and each pop checks the 4 directions with O(1) work. Scan plus checks: R · C + 4 · R · C = 5 · R · C = O(R * C).

SPACE COMPLEXITY

O(R * C)

queue can hold every rotten cell at once (for example all of them at minute 0), so O(R * C). The grid is updated in place and there is no recursion (call stack O(1)); the answer is one integer.

Formal Recurrence Relation

T(R, C) = R · C (scan) + 4 · R · C (neighbour checks) = 5 · R · C = O(R * C)

Derivation Progression

Seed scan

for r in range(R): for c in range(C) → R · C cells

Every rotten orange is pushed and every fresh one counted, O(1) per cell.

Spread

grid[nr][nc] = 2 before queue.append((nr, nc)) → ≤ R · C pops × 4 directions

A cell is pushed only when it turns rotten, which happens once, so there are at most R · C pops with 4 checks each.

Total

R · C + 4 · R · C = 5 · R · C = O(R * C)

Both phases are linear in the number of cells.

Variable Definitions

RRR

Number of rows in the grid

CCC

Number of columns in the grid

Memory Architecture & Bounds

🟣 Call Stack

O(1): no recursion

🔵 Auxiliary Heap

O(R * C): queue of coordinates; the grid is changed in place

🟢 Output Space

O(1): the minutes count

Boundary Best / Worst Cases

Best Case

O(R∗C)O(R * C)O(R∗C)

Average Case

O(R∗C)O(R * C)O(R∗C)

Worst Case

O(R∗C)O(R * C)O(R∗C)

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: "Each minute, rot spreads" from every rotten orange, "how many minutes must pass before every orange is rotten": many sources on one clock, Multi-Source BFS. At scale the same shape is a nearest-source distance map computed for a whole grid in one pass.

CONSTRAINTS & BOUNDS

m,n≤10m, n \le 10m,n≤10, so at most 100 cells and 500 steps. The cost is linear in cells whatever the number of sources: a 104×10410^4 \times 10^4104×104 map is 10810^8108 cells, where the queue (one coordinate pair per cell of the widest ring) and the grid itself are the memory to plan for.

FAANG PRODUCTION TRAPS & EDGE CASES

The grid is changed in place (fresh cells become 2): copy it first if the caller still needs the original. On a map too large for memory, the rings can be processed in tiles, but a ring crossing a tile border must be handed to the neighbouring tile in the same round.

Core Algorithmic State Invariants

  1. First Arrival Is the Nearest Source

All sources start in round 0 and every ring grows by one cell per round, so the first time any ring reaches a cell, no other source is closer. That is why the round a cell turns rotten is its distance to the nearest rotten orange, and why it never needs updating.

Theory Context•Breadth-First Search (BFS)
MediumLC 994

Rotting Oranges (LeetCode 994)

Target Frequency:AmazonGoogleMeta

You're given an m x n grid whose cells each hold one of three values: 0 for an empty cell, 1 for a fresh orange, and 2 for a rotten one.

Each minute, rot spreads: any fresh orange touching a rotten orange along one of the four cardinal directions turns rotten too.

Work out how many minutes must pass before every orange is rotten, and return -1 if some fresh orange can never be reached.

Worked Examples

Example 1
Input:grid = [[2,1,1],[1,1,0],[0,1,1]]
Output:4
211110011
Explanation: Rot spreads one step per minute; the orange in the bottom-right corner is the last to turn, at minute 4.
Example 2
Input:grid = [[2,1,1],[0,1,1],[1,0,1]]
Output:-1
Explanation: The orange at row 2, column 0 stays fresh forever, since rot only spreads through direct up/down/left/right contact, not diagonally.
Example 3
Input:grid = [[0,2]]
Output:0
Explanation: There are no fresh oranges to begin with, so the elapsed time is 0.

⚖️Formal Constraints & Bounds

  • m == grid.length

  • n == grid[i].length

  • 1 <= m, n <= 10

  • grid[i][j] is 0, 1, or 2.

Deep-Dive & Conceptual Insights

Core Insight

Marking oranges rotten at enqueue time prevents duplicate queue entries and models parallel infection waves accurately.

Real-World Scenario & Production Applications

Epidemiological virus contagion modeling and wildfire spread perimeter simulations.

Subproblems & Recurrence Decomposition3 Phases

🧩Subproblem 1: Multi-Source Origin Seeding

Scan the entire grid. Enqueue all initial source coordinates (e.g. all rotten oranges, distance 0) and count target elements.

Mathematical Recurrence / Code Invariant
queue = deque()
fresh = 0
for r in range(R):
    for c in range(C):
        if grid[r][c] == 2: queue.append((r, c))
        elif grid[r][c] == 1: fresh += 1

Step-by-Step Execution Trace Table

Code / Blueprint
Minute 0 (Initial Seed): Minute 1 (Infect): Minute 2 (Infect):
[ 2 ] [ 1 ] [ 1 ] [ 2 ] [ 2 ] [ 1 ] [ 2 ] [ 2 ] [ 2 ]
[ 1 ] [ 1 ] [ 0 ] ==> [ 2 ] [ 1 ] [ 0 ] ==> [ 2 ] [ 2 ] [ 0 ]
[ 0 ] [ 1 ] [ 1 ] [ 0 ] [ 1 ] [ 1 ] [ 0 ] [ 1 ] [ 1 ]
queue=[(0,0)], fresh=6 queue=[(0,1),(1,0)], fresh=4 queue=[(0,2),(1,1)], fresh=2
 
Minute 3 (Infect): Minute 4 (Final Fresh):
[ 2 ] [ 2 ] [ 2 ] [ 2 ] [ 2 ] [ 2 ]
[ 2 ] [ 2 ] [ 0 ] ==> [ 2 ] [ 2 ] [ 0 ]
[ 0 ] [ 2 ] [ 1 ] [ 0 ] [ 2 ] [ 2 ]
queue=[(2,1)], fresh=1 queue=[(2,2)], fresh=0 (Goal Reached! ✅)
 
Step-by-Step Wavefront Progression
  1. Step 1 (Multi-Source Seed, Minute 0, d=0): queue = [(0, 0)], fresh = 6. Scan grid at t=0t=0t=0: count 6 fresh oranges, enqueue initial rotten cell (0, 0).
  2. Step 2 (Snapshot Minute 0, level_size=1, d=0): level_size = 1. Minute 0 wavefront starts.
  3. Step 3 (Pop (0,0) & Infect (0,1), (1,0), d=0): Pop (0, 0). 4-directional neighbors: infect (0, 1) and (1, 0). Mark on enqueue: grid[0][1]=2, grid[1][0]=2. fresh = 4. queue = [(0, 1), (1, 0)]. Minute advances: minutes = 1 🌊.
  4. Step 4 (Snapshot Minute 1, level_size=2, d=1): level_size = 2. Minute 1 wavefront. queue = [(0, 1), (1, 0)].
  5. Step 5 (Pop (0,1) & Infect (0,2), (1,1), d=1): Pop (0, 1). Infect (0, 2) and (1, 1). Mark on enqueue →\rightarrow→ fresh = 2. queue = [(1, 0), (0, 2), (1, 1)].
  6. Step 6 (Pop (1,0), Diamond Convergence Guard, d=1): Pop (1, 0). Neighbor (1, 1) is already marked 2 (visited)! Guard prevents duplicate enqueue. Minute advances: minutes = 2 🌊.
  7. Step 7 (Snapshot Minute 2, level_size=2, d=2): level_size = 2. Minute 2 wavefront. queue = [(0, 2), (1, 1)].
  8. Step 8 (Pop (0,2), Blocked Neighbor (1,2)=0, d=2): Pop (0, 2). Neighbor (1, 2) is empty (0) →\rightarrow→ skip.
  9. Step 9 (Pop (1,1) & Infect (2,1), d=2): Pop (1, 1). Infect (2, 1). Mark on enqueue →\rightarrow→ fresh = 1. queue = [(2, 1)]. Minute advances: minutes = 3 🌊.
  10. Step 10 (Snapshot Minute 3, level_size=1, d=3): level_size = 1. Minute 3 wavefront. queue = [(2, 1)].
  11. Step 11 (Pop (2,1) & Infect Final (2,2), d=3): Pop (2, 1). Infect (2, 2). Mark on enqueue →\rightarrow→ fresh = 0. queue = [(2, 2)]. Minute advances: minutes = 4 🌊.
  12. Step 12 (Goal Check, fresh == 0, Complete, d=4): fresh = 0. All oranges fully rotted. Search Complete: return 4 ✅.
Full Walkthrough12 Steps
Input:grid = [[2,1,1],[1,1,0],[0,1,1]]Expected:4
1🌱 ROOT(Multi-Source Seed, Minute 0, d=0)
queue = [(0, 0)]
queue = [(0, 0)], fresh = 6. Scan grid at t=0t=0t=0: count 6 fresh oranges, enqueue initial rotten cell (0, 0).
2🌱 ROOT(Snapshot Minute 0, level_size=1, d=0)
level_size = 1
level_size = 1. Minute 0 wavefront starts.
3🌱 ROOT(Pop (0,0) & Infect (0,1), (1,0), d=0)
queue = [(0, 1), (1, 0)]
Pop (0, 0). 4-directional neighbors: infect (0, 1) and (1, 0). Mark on enqueue: grid[0][1]=2, grid[1][0]=2. fresh = 4. queue = [(0, 1), (1, 0)]. Minute advances: minutes = 1 🌊.
4📸 SNAPSHOT(Snapshot Minute 1, level_size=2, d=1)
queue = [(0, 1), (1, 0)]
level_size = 2. Minute 1 wavefront. queue = [(0, 1), (1, 0)].
5🌊 ENQUEUE / DISCOVER(Pop (0,1) & Infect (0,2), (1,1), d=1)
queue = [(1, 0), (0, 2), (1, 1)]
Pop (0, 1). Infect (0, 2) and (1, 1). Mark on enqueue →\rightarrow→ fresh = 2. queue = [(1, 0), (0, 2), (1, 1)].
6🌊 ENQUEUE / DISCOVER(Pop (1,0), Diamond Convergence Guard, d=1)
Pop (1, 0). Neighbor (1, 1) is already marked 2 (visited)! Guard prevents duplicate enqueue. Minute advances: minutes = 2 🌊.
7📸 SNAPSHOT(Snapshot Minute 2, level_size=2, d=2)
queue = [(0, 2), (1, 1)]
level_size = 2. Minute 2 wavefront. queue = [(0, 2), (1, 1)].
8⚡ FIFO DRAIN(Pop (0,2), Blocked Neighbor (1,2)=0, d=2)
Pop (0, 2). Neighbor (1, 2) is empty (0) →\rightarrow→ skip.
9🌊 ENQUEUE / DISCOVER(Pop (1,1) & Infect (2,1), d=2)
queue = [(2, 1)]
Pop (1, 1). Infect (2, 1). Mark on enqueue →\rightarrow→ fresh = 1. queue = [(2, 1)]. Minute advances: minutes = 3 🌊.
10📸 SNAPSHOT(Snapshot Minute 3, level_size=1, d=3)
queue = [(2, 1)]
level_size = 1. Minute 3 wavefront. queue = [(2, 1)].
11🌊 ENQUEUE / DISCOVER(Pop (2,1) & Infect Final (2,2), d=3)
queue = [(2, 2)]
Pop (2, 1). Infect (2, 2). Mark on enqueue →\rightarrow→ fresh = 0. queue = [(2, 2)]. Minute advances: minutes = 4 🌊.
12✅ RECORD / GOAL(Goal Check, fresh == 0, Complete, d=4)
fresh = 0
fresh = 0. All oranges fully rotted. Search Complete: return 4 ✅.
Code Shape
Archetype Code Shape
Python
1queue = deque(all_rotten_cells)
2fresh = count_fresh()
3minutes = 0
4while queue and fresh > 0:
5 for _ in range(len(queue)):
6 r, c = queue.popleft()
7 make each fresh neighbor in the 4 directions rotten, fresh -= 1, and append it to queue
8 minutes += 1
9return minutes if fresh == 0 else -1

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Every earlier BFS in this pattern started from exactly one node. Here, the infection starts from potentially dozens of places at the same instant — and the fix is almost embarrassingly simple.

🪨 The Analogy: Dropping Several Stones in a Pond at Once

Picture dropping several stones into a pond at the exact same moment, instead of just one. Each stone starts its own ripple, and all the ripples expand outward in perfect sync — where two ripples meet, both arrived at that point during the very same "tick." You don't need to simulate each stone's ripple separately and then merge them; if you drop all the stones into the water before the clock starts, a single shared expanding wavefront naturally emerges.

🔑 The Key Idea in the Code

Seeding the queue with every starting point before the first level even begins is the entire adaptation — the level-by-level snapshot loop doesn't need to change at all; it just now represents "everyone one minute away from any rotten orange," not "everyone one step from a single source."

  • Single-source BFS instead of multi-source wavefront: Running BFS from one rotten orange at a time fails to model concurrent infection. All initial rotten oranges must be enqueued at minute 0 simultaneously.

  • Incrementing timer when no fresh oranges rot: Incrementing the minute counter on every level even when no adjacent fresh oranges were infected overcounts total minutes.

  • Isolated fresh oranges unreached: Forgetting to check if fresh_count > 0 after the queue is empty, returning elapsed minutes when fresh oranges were actually unreachable (must return -1).

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"Each minute, rot spreads" from every rotten orange at once, and the question is "how many minutes must pass before every orange is rotten", or -1. Many sources, one shared clock: Multi-Source BFS.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

After minutes rounds, queue holds exactly the oranges that turned rotten in round minutes, and fresh_count counts the fresh oranges not reached yet. The decision: a fresh neighbour turns rotten (grid[nr][nc] = 2) and is pushed in the round that first reaches it.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • return minutes if fresh_count == 0 else -1: a fresh orange walled off by empty cells is never reached, so the answer is -1, not the minutes counted so far.

  • while queue and fresh_count > 0: the card's trap; without fresh_count > 0 the last round reaches nothing new and adds one minute too many.

  • if fresh_count == 0: return 0: a grid with no fresh orange needs 0 minutes, even when it has no rotten orange either.

The 60-Second Interview Pitch

Say this out loud before you type a single line

This is Multi-Source BFS. Every rotten orange spreads at the same time, so I put all of them in the queue at minute zero and count the fresh ones. Then I run level-by-level BFS: each round is one minute, and in that round every orange in the queue turns its fresh neighbours rotten, which I mark right away and push for the next round. Because all sources grow one ring per round together, each fresh orange rots in the round of its nearest rotten orange, so the round count is the answer. The trap is the last round: I only start a round while fresh oranges remain, otherwise a final round that reaches nothing adds one minute too many. If the queue empties with fresh oranges left, some are walled off, and I return minus one. Each cell is pushed at most once, so it's O(R times C) time and space.

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(R * C)

Look at the code: the double for r in range(R): for c in range(C) scan visits each of the R · C cells once. In while queue and fresh_count > 0, a cell is pushed only when it turns from 1 to 2, so each cell is pushed at most once, and each pop checks the 4 directions with O(1) work. Scan plus checks: R · C + 4 · R · C = 5 · R · C = O(R * C).

SPACE COMPLEXITY

O(R * C)

queue can hold every rotten cell at once (for example all of them at minute 0), so O(R * C). The grid is updated in place and there is no recursion (call stack O(1)); the answer is one integer.

Formal Recurrence Relation

T(R, C) = R · C (scan) + 4 · R · C (neighbour checks) = 5 · R · C = O(R * C)

Derivation Progression

Seed scan

for r in range(R): for c in range(C) → R · C cells

Every rotten orange is pushed and every fresh one counted, O(1) per cell.

Spread

grid[nr][nc] = 2 before queue.append((nr, nc)) → ≤ R · C pops × 4 directions

A cell is pushed only when it turns rotten, which happens once, so there are at most R · C pops with 4 checks each.

Total

R · C + 4 · R · C = 5 · R · C = O(R * C)

Both phases are linear in the number of cells.

Variable Definitions

RRR

Number of rows in the grid

CCC

Number of columns in the grid

Memory Architecture & Bounds

🟣 Call Stack

O(1): no recursion

🔵 Auxiliary Heap

O(R * C): queue of coordinates; the grid is changed in place

🟢 Output Space

O(1): the minutes count

Boundary Best / Worst Cases

Best Case

O(R∗C)O(R * C)O(R∗C)

Average Case

O(R∗C)O(R * C)O(R∗C)

Worst Case

O(R∗C)O(R * C)O(R∗C)

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: "Each minute, rot spreads" from every rotten orange, "how many minutes must pass before every orange is rotten": many sources on one clock, Multi-Source BFS. At scale the same shape is a nearest-source distance map computed for a whole grid in one pass.

CONSTRAINTS & BOUNDS

m,n≤10m, n \le 10m,n≤10, so at most 100 cells and 500 steps. The cost is linear in cells whatever the number of sources: a 104×10410^4 \times 10^4104×104 map is 10810^8108 cells, where the queue (one coordinate pair per cell of the widest ring) and the grid itself are the memory to plan for.

FAANG PRODUCTION TRAPS & EDGE CASES

The grid is changed in place (fresh cells become 2): copy it first if the caller still needs the original. On a map too large for memory, the rings can be processed in tiles, but a ring crossing a tile border must be handed to the neighbouring tile in the same round.

Core Algorithmic State Invariants

  1. First Arrival Is the Nearest Source

All sources start in round 0 and every ring grows by one cell per round, so the first time any ring reaches a cell, no other source is closer. That is why the round a cell turns rotten is its distance to the nearest rotten orange, and why it never needs updating.

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