题目描述
网格里 2=烂、1=好、0=空。每分钟烂橘子让上下左右相邻的好橘子变烂。几分钟后全烂?
思路解析
转折:普通 BFS 从一个点出发,这里腐烂是同时从多处发生的。所以把所有初始烂橘子一次性放进队列,再用 for _ in range(len(q)) 一次只处理「这一层」,每推进一整层就过去一分钟。开始前先数一遍好橘子 fresh,最后用它判断是否全烂。
第 0 分钟 · 标源 + 数 fresh:扫一遍:好橘子 fresh=6(绿),唯一的烂橘子 (0,0)(橙)入队,灰色 (1,2)、(2,0) 是空格。计时 0 分钟。
准备处理第 1 层 · 出队 (0,0):本层只有 (0,0)。看它上下左右:上、左越界,右 (0,1) 和下 (1,0) 是好橘子,要被传染。计时还停在 0——等这层邻居真的烂了,才算过去 1 分钟。
第 1 分钟 · 右/下变烂:(0,1)、(1,0) 变烂(高亮),fresh 6→4,它们入队等下一层。第 1 分钟结束。
第 2 分钟 · 这一层一起扩散:本层 (0,1)、(1,0) 同时传染各自的好邻居:(0,2) 和 (1,1) 变烂,fresh 4→2。
第 2 分钟 · 空格挡路(负例):负例:(1,1) 想往右传到 (1,2)、(1,0) 想往下传到 (2,0),但这两格是空格 0,腐烂传不过去,直接跳过。只有值为 1 的好橘子才会被传染。
第 3 分钟 · 绕过空格继续:第 3 分钟:(1,1) 往下把 (2,1) 传染变烂,fresh 2→1。
第 4 分钟 · 最后一个变烂:第 4 分钟:(2,1) 把最后的 (2,2) 传染,fresh 1→0。
检查 fresh==0 → 返回 4:队列空了,检查 fresh==0:全烂!返回 4 分钟。若还剩好橘子(被空格隔绝),就返回 -1。
以后你遇到「同时从多处扩散、求最短时间/距离」的题,就用多源 BFS:01 矩阵、地图分析都是它。
参考代码(Python)
1q = deque()
2for 每个格子:
3 if 是烂橘子: q.append((i,j)) # 所有烂橘子一起入队
4 elif 是好橘子: fresh += 1
5while q and fresh > 0:
6 level += 1 # 过去一分钟
7 for _ in range(len(q)): # 只处理class="cl-str">"这一层"
8 i,j = q.popleft()
9 for 四个方向的好邻居:
10 变烂; fresh -= 1; q.append(邻居)
11return level if fresh == 0 else -1复杂度分析
- 时间复杂度:O(m×n) —— 每格进出队一次
- 空间复杂度:O(m×n) —— 队列最多装一层
套路模板
记住骨架:所有起点一次性入队、for _ in range(len(q)) 锁层、每层 +1。01 矩阵、地图分析都是把多个源同时入队,和单源 BFS 只差「起点个数」。
1# 「同时从多个源扩散、求最短时间/距离」都套
2q = deque(所有起点); seen = set(所有起点)
3level = 0
4while q:
5 for _ in range(len(q)): # 锁定class="cl-str">"这一层"
6 x = q.popleft()
7 for y in 邻居(x):
8 if y not in seen: seen.add(y); q.append(y)
9 level += 1 # 一层 = 一个时间单位易错点
- 错误写法:只把一个烂橘子入队 → 正确写法:初始所有烂橘子都入队(它们是同时开始腐烂的)
- 错误写法:不按层、直接数步数 → 正确写法:for _ in range(len(q)) 锁定当前层(否则分不清「第几分钟」)
- 错误写法:忘了判断剩余好橘子 → 正确写法:最后 fresh>0 返回 -1(有的好橘子被空格隔绝,永远烂不了)
以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握。