🏗️ System Design — Synthesis (Expert Phase, Day 106)
Saturday — Deep Dive day. System Design section is in synthesis mode (all 60 curated topics exhausted).
Today's Synthesis Note
Today's Saturday Deep Dive focuses on 图遍历模式 — BFS/DFS 精讲 (Graph Traversal Patterns). The full deep-dive content is in 2026-08-01-deepdive.md (./2026-08-01-deepdive.md).
Graph traversal underpins many system design concepts covered this program: CDN cache invalidation (multi-source BFS), microservice dependency detection (topological sort), geospatial systems (BFS on grids), and distributed systems failure propagation analysis.
🧮 Rotting Oranges (LeetCode #994) — BFS Deep Dive
Note: This is a Saturday. The full content for this topic is in the Saturday Deep Dive (./2026-08-01-deepdive.md). This file is a reference stub.
Saturday — Deep Dive day. Behavioral section is in synthesis mode (all 60 curated questions exhausted).
Today's Synthesis Note
Today's Saturday Deep Dive covers graph traversal patterns. The full deep-dive content is in 2026-08-01-deepdive.md (./2026-08-01-deepdive.md).
Synthesis theme: Graph-structured thinking appears in engineering leadership too — mapping team dependencies (topological sort), understanding how failures propagate across teams (BFS), and finding shortest paths to unblock teammates. The mental models transfer.
🤖 AI Concepts — Synthesis (Expert Phase, Day 106)
Saturday — Deep Dive day. AI section is in synthesis mode (all 30 curated concepts exhausted).
Today's Synthesis Note
Today's Saturday Deep Dive covers graph traversal patterns. The full deep-dive content is in 2026-08-01-deepdive.md (./2026-08-01-deepdive.md).
Synthesis note: Graph traversal is foundational to modern AI systems: knowledge graphs use BFS for entity relationship traversal; RAG pipelines use graph-based chunking; neural network backpropagation is a topological sort over the computation graph; agent planning (ReAct) builds DAGs of tool calls. The patterns learned today are literally inside every major AI framework.
🔬 Saturday Deep Dive: 图遍历模式 — BFS/DFS 精讲 (15 min read)
Graph traversal is the backbone of coding interviews. Nearly every problem involving "connectivity," "shortest path," or "dependencies" is secretly a graph problem. Master BFS/DFS, and you unlock: island counting, course scheduling, word ladders, rotting oranges, and much more.
核心问题 / Core question: 给定起点,你能到达哪里?到达需要多少步?图是否有环?
Core question: From a starting node, what can you reach? In how many steps? Does the graph have a cycle?
Grid:0=empty, 1=fresh orange, 2=rotten orange. Each minute, rotten oranges rot adjacent fresh ones. Return minimum minutes to rot all, or -1 if impossible.
from collections import deque
def orangesRotting(grid: list[list[int]]) -> int:
rows, cols = len(grid), len(grid[0])
DIRS = [(0, 1), (0, -1), (1, 0), (-1, 0)]
# Step 1: Find all initial rotten oranges (multi-sources)# and count fresh oranges
queue = deque()
fresh_count = 0for r in range(rows):
for c in range(cols):
if grid[r][c] == 2:
queue.append((r, c, 0)) # (row, col, time)elif grid[r][c] == 1:
fresh_count += 1# Edge case: no fresh oranges at allif fresh_count == 0:
return0# Step 2: Multi-source BFS — all rotten oranges spread simultaneously
max_time = 0while queue:
r, c, time = queue.popleft()
for dr, dc in DIRS:
nr, nc = r + dr, c + dc
# Check bounds and freshnessif (0 <= nr < rows and0 <= nc < cols and
grid[nr][nc] == 1): # only fresh oranges
grid[nr][nc] = 2# mark as rotten (in-place = visited)
fresh_count -= 1# one fewer fresh orange
max_time = max(max_time, time + 1)
queue.append((nr, nc, time + 1))
# Step 3: If fresh_count > 0, some oranges are unreachablereturn max_time if fresh_count == 0else -1
All sources start simultaneously: Add all initial rotten oranges to the queue at once. They spread at the same rate, so BFS naturally finds the minimum time.
In-place visited marking: Mutate grid[nr][nc] = 2 to mark visited — no extra set needed.
最终检查:用 fresh_count 判断是否有孤岛(被 0 包围的新鲜橘子)。
Final check:fresh_count > 0 means isolated fresh oranges exist (surrounded by empty cells).
2.3 相关变体 — 从"腐烂橘子"到"最短路径" / Variants
Walls and Gates (#286):把所有 gates(值为0的格子)同时加入队列,BFS 向外扩散填写距离。和腐烂橘子完全同构。
Walls and Gates: Add all gates simultaneously, BFS outward to fill distances. Identical structure to Rotting Oranges.
def wallsAndGates(rooms):
rows, cols = len(rooms), len(rooms[0])
DIRS = [(0,1),(0,-1),(1,0),(-1,0)]
INF = 2**31 - 1
queue = deque()
for r in range(rows):
for c in range(cols):
if rooms[r][c] == 0: # gate = source
queue.append((r, c))
while queue:
r, c = queue.popleft()
for dr, dc in DIRS:
nr, nc = r + dr, c + dc
if (0 <= nr < rows and0 <= nc < cols and
rooms[nr][nc] == INF): # unvisited room
rooms[nr][nc] = rooms[r][c] + 1
queue.append((nr, nc))
2.4 DFS 的经典用法 — 岛屿计数 / Classic DFS: Number of Islands
def numIslands(grid: list[list[str]]) -> int:
ifnot grid:
return0
rows, cols = len(grid), len(grid[0])
count = 0def dfs(r, c):
# Base cases: out of bounds or water or already visitedif (r < 0or r >= rows or
c < 0or c >= cols or
grid[r][c] != '1'):
return
grid[r][c] = '0'# mark visited (sink the land)
dfs(r+1, c)
dfs(r-1, c)
dfs(r, c+1)
dfs(r, c-1)
for r in range(rows):
for c in range(cols):
if grid[r][c] == '1':
count += 1
dfs(r, c) # flood-fill this islandreturn count
DFS is more natural here: We don't care about distances, only the number of connected components. Each time we hit land, DFS floods the whole island, count += 1.
One of graph theory's most powerful tools: topological sort on a DAG solves dependency ordering problems.
from collections import deque
def canFinish(numCourses: int, prerequisites: list[list[int]]) -> bool:
# Build adjacency list and in-degree array
graph = [[] for _ in range(numCourses)]
in_degree = [0] * numCourses
for course, prereq in prerequisites:
graph[prereq].append(course) # prereq → course
in_degree[course] += 1# Kahn's Algorithm: start with nodes that have no prerequisites
queue = deque()
for i in range(numCourses):
if in_degree[i] == 0:
queue.append(i)
completed = 0while queue:
course = queue.popleft()
completed += 1for next_course in graph[course]:
in_degree[next_course] -= 1if in_degree[next_course] == 0: # all prereqs satisfied
queue.append(next_course)
# If we completed all courses, no cycle existsreturn completed == numCourses
直觉 / Intuition: 拓扑排序 = BFS on in-degree。每次取"没有前置依赖的节点",处理后更新其邻居的 in-degree。如果最终处理了所有节点,说明无环。
Intuition: Topological sort = BFS on in-degrees. Take nodes with no remaining prerequisites, process them, then reduce their successors' in-degrees. If all nodes processed → no cycle.
Part 3: Edge Cases & Gotchas / 边界情况 (2 min)
🚨 常见错误 / Common Mistakes
1. BFS 出队时才 mark visited(不是入队时)
# ❌ WRONG — same node enqueued multiple timeswhile queue:
node = queue.popleft()
visited.add(node) # too late!for neighbor in graph[node]:
if neighbor notin visited:
queue.append(neighbor)
# ✅ CORRECT — mark on enqueuewhile queue:
node = queue.popleft()
for neighbor in graph[node]:
if neighbor notin visited:
visited.add(neighbor) # mark on enqueue
queue.append(neighbor)
2. 网格越界检查顺序错误(短路求值救你)
# ✅ bounds check BEFORE accessing grid[nr][nc]if0 <= nr < rows and0 <= nc < cols and grid[nr][nc] == 1:
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^# Python short-circuits: if bounds fail, grid access never happens
3. 多源 BFS 忘记处理"全部已腐烂"边界
if fresh_count == 0:
return0# Don't run BFS at all — answer is already 0
4. 拓扑排序中漏掉孤立节点
初始化时,所有 in-degree == 0 的节点(包括没有任何依赖的独立课程)都要加入队列。
When initializing, add ALL nodes with in-degree == 0 to the queue, including isolated nodes with no edges.
Real example: When a Netflix microservice fails, the SRE tooling uses BFS on the service dependency graph to compute "affected downstream services" — structurally identical to Rotting Oranges' multi-source BFS.
A: BFS expands level by level, correctly simulating "all oranges rot simultaneously." DFS would go deep in one direction first, giving wrong timing. For "minimum time/steps," always reach for BFS first.
Q2: 什么时候用多源 BFS?
Q2: When do you use multi-source BFS?
A: 当有多个起点,且你想知道每个节点到"最近起点"的距离时。实现方式:初始化时将所有起点同时入队(距离=0),之后标准 BFS 即可。同一个框架适用于:腐烂橘子、Walls and Gates、矩阵中的 01 矩阵(#542)。
A: When you have multiple sources and want each node's distance to the nearest source. Initialize by enqueuing all sources at distance 0, then run standard BFS. Works for: Rotting Oranges, Walls and Gates, 01 Matrix (#542).
Q3: 如何判断一道题是 BFS 还是 DFS?
Q3: How do you decide between BFS and DFS?
A: 经验法则:
需要"最短路径"或"最少步数" → BFS
需要"是否可达"或"连通分量数" → DFS 或 BFS 均可,DFS 递归更简洁
需要"所有路径"或"回溯" → DFS
有拓扑依赖/环检测 → BFS(Kahn)或 DFS(颜色标记)
A (rule of thumb):
"Shortest path / minimum steps" → BFS
"Reachability / connected components" → either; DFS is simpler recursively
"All paths / backtracking" → DFS
"Topological order / cycle detection" → BFS (Kahn's) or DFS (coloring)
Q4: 如果图很大(100万节点),递归 DFS 会有什么问题?
Q4: What's the problem with recursive DFS on a very large graph?
Kahn (BFS): Iterative, outputs topological order directly, simple cycle detection (completed == numNodes). Preferred in production. Also supports lexicographically smallest order with a min-heap.
DFS topological sort: Recursive, uses post-order (push on finish), shorter code but needs three-color marking (white/gray/black) for cycle detection. Better when recursive style is preferred.