想系统提升编程能力、查看更完整的学习路线,欢迎访问 AI Compass:github.com/tingaicompa… 仓库持续更新刷题题解、Python 基础和 AI 实战内容,适合想高效进阶的你。
📖 第90课:腐烂的橘子
模块:图论 | 难度:Medium ⭐⭐ LeetCode 链接:leetcode.cn/problems/ro… 前置知识:第44课(BFS层序遍历)、第89课(岛屿数量) 预计学习时间:25分钟
🎯 题目描述
在一个 m × n 的网格中,每个格子可以是以下三个值之一:
0表示空格子1表示新鲜橘子2表示腐烂的橘子
每分钟,腐烂的橘子会使其上下左右四个方向的新鲜橘子腐烂。返回直到没有新鲜橘子为止所必须经过的最小分钟数。如果不可能使所有橘子腐烂,返回 -1。
示例:
输入:grid = [
[2,1,1],
[1,1,0],
[0,1,1]
]
输出:4
解释:
初始:左上角橘子已腐烂
第1分钟:腐烂扩散到(0,1)和(1,0)
第2分钟:扩散到(0,2)和(1,1)和(2,1)
第3分钟:扩散到(1,2)
第4分钟:扩散到(2,2)
全部腐烂,用时4分钟
约束条件:
m == grid.lengthn == grid[i].length1 <= m, n <= 10grid[i][j]的值为0、1或2
🧪 边界用例(面试必考)
| 用例类型 | 输入 | 期望输出 | 考察点 |
|---|---|---|---|
| 全是新鲜 | [[1,1],[1,1]] | -1 | 无腐烂源头,无法完成 |
| 已全腐烂 | [[2,2],[2,2]] | 0 | 初始就完成,用时0 |
| 有隔离 | [[2,1,0,1]] | -1 | 空格子隔断,右侧橘子永远新鲜 |
| 多源同时扩散 | [[2,1,1],[0,1,1],[1,0,2]] | 2 | 两个腐烂源同时扩散 |
| 无新鲜橘子 | [[0,2]] | 0 | 没有新鲜橘子,返回0 |
💡 思路引导
生活化比喻
想象你在一个水果仓库,有几箱橘子已经开始腐烂...
🐌 笨办法:每分钟检查所有橘子,把腐烂橘子旁边的新鲜橘子标记为"下一分钟要腐烂",然后重复这个过程。这样需要反复遍历整个仓库,效率很低。
🚀 聪明办法:把所有"已腐烂的橘子"位置记录在一个清单上,每分钟只检查清单中的橘子,把它们周围的新鲜橘子加入"下一分钟清单"。这样每个橘子最多被检查一次,像波浪一样从所有腐烂源头同时向外扩散,直到覆盖所有能到达的橘子。
关键洞察
这是一个"多源BFS最短路径"问题:所有腐烂橘子同时向外扩散,每一层代表一分钟,层数就是所需时间
🧠 解题思维链
这一节模拟你在面试中"从零开始思考"的过程。
Step 1:理解题目 → 锁定输入输出
- 输入:二维整数数组
grid,元素为0、1、2 - 输出:整数,表示所有新鲜橘子腐烂所需的最小分钟数;若不可能则返回
-1 - 限制:腐烂是"同时扩散"的(所有腐烂橘子在同一分钟内各自向四周扩散)
Step 2:先想笨办法(暴力模拟)
每分钟遍历整个网格,找到所有腐烂橘子 2,将它们周围的新鲜橘子 1 改为 2,重复直到没有变化。
- 时间复杂度:O(m × n × T),T 是总分钟数,最坏 O((m×n)²)
- 瓶颈在哪:每分钟都要全网格扫描,大量重复检查
Step 3:瓶颈分析 → 优化方向
核心问题:"如何避免重复扫描已处理的格子?"
- 关键:用队列记录"当前这一分钟要处理的腐烂橘子",下一分钟只处理新腐烂的橘子
- 优化思路:多源BFS(Multi-Source BFS)—— 将所有初始腐烂橘子作为起点,同时开始BFS
Step 4:选择武器
- 选用:BFS队列 + 分层遍历
- 理由:
- BFS天然按"层"扩展,每层代表一分钟
- 多源BFS:初始时将所有腐烂橘子加入队列,之后逐层扩散
- 时间复杂度优化到 O(m × n)
🔑 模式识别提示:当题目出现"同时扩散""最少步数""多个起点"等关键词,优先考虑 多源BFS 模式
🔑 解法一:多源BFS(标准解法)
思路
- 初始化:遍历网格,将所有腐烂橘子
2的坐标加入队列,统计新鲜橘子1的数量 - BFS分层扩散:每次处理完队列中的一层(一分钟),时间 +1
- 检查结果:若还有新鲜橘子剩余,返回
-1;否则返回分钟数
图解过程
示例: [[2,1,1],[1,1,0],[0,1,1]]
初始化:
queue = [(0,0)] <- 初始腐烂橘子
fresh_count = 6 <- 新鲜橘子数量
minutes = 0
第1分钟:
处理 (0,0),扩散到 (0,1) 和 (1,0)
queue = [(0,1), (1,0)]
fresh_count = 4
minutes = 1
网格状态:
[2, 2, 1]
[2, 1, 0]
[0, 1, 1]
第2分钟:
处理 (0,1) -> 扩散到 (0,2)
处理 (1,0) -> 扩散到 (1,1)
queue = [(0,2), (1,1), (2,1)] <- (2,1)也被(1,1)扩散到
fresh_count = 1
minutes = 2
网格状态:
[2, 2, 2]
[2, 2, 0]
[0, 2, 1]
第3分钟:
处理 (0,2) -> 无新扩散
处理 (1,1) -> 扩散到 (1,2)
处理 (2,1) -> 扩散到 (2,2)
queue = [(1,2), (2,2)]
fresh_count = 0 <- 注意:(1,2)和(2,2)在这一分钟都腐烂了,但还在队列中
实际上这里有个细节:第3分钟处理完后,队列还有元素,需要再处理一轮
修正:
第3分钟:
处理3个元素后,queue = [(1,2)]
minutes = 3
第4分钟:
处理 (1,2) -> 扩散到 (2,2)
queue = [(2,2)]
minutes = 4
第5分钟:
处理 (2,2) -> 无新扩散
queue = []
结束
实际上,正确的分层处理应该是:
第1分钟结束时,腐烂的是 (0,1) 和 (1,0),fresh_count = 4
第2分钟结束时,腐烂的是 (0,2)、(1,1)、(2,1),fresh_count = 1
第3分钟结束时,腐烂的是 (1,2),fresh_count = 0
等等,让我重新梳理:
初始:grid[0][0]=2,其余6个是1
第1分钟:(0,1)和(1,0)腐烂,剩余4个新鲜
第2分钟:(0,2)、(1,1)、(2,1)腐烂,剩余1个新鲜
第3分钟:(1,2)腐烂(从(1,1)和(2,1)同时扩散到),剩余0个新鲜
不对,题目示例说是4分钟。让我重新看:
实际路径:
(0,0)[初始] -> (0,1)[1分] -> (0,2)[2分]
(1,0)[1分] -> (1,1)[2分] -> (1,2)[3分] -> (2,2)[4分]
(2,1)[2分] /
所以是4分钟正确。关键是 (2,2) 需要从 (1,2) 扩散,而 (1,2) 在第3分钟才腐烂。
重新整理:
初始:
2 1 1
1 1 0
0 1 1
第0分钟结束(初始状态):queue = [(0,0)], fresh = 6
第1分钟结束:
2 2 1
2 1 0
0 1 1
从(0,0)扩散到(0,1)和(1,0),fresh = 4
第2分钟结束:
2 2 2
2 2 0
0 2 1
从(0,1)扩散到(0,2),从(1,0)扩散到(1,1),从(1,1)扩散到(2,1),fresh = 1
第3分钟结束:
2 2 2
2 2 2
0 2 1
从(1,1)扩散到(1,2),fresh = 1 (等等,(1,2)已腐烂,为什么还剩1个?)
重新数:初始新鲜橘子位置:
(0,1), (0,2), (1,0), (1,1), (2,1), (2,2) 共6个
第1分钟:(0,1)和(1,0)腐烂,剩余4个
第2分钟:(0,2), (1,1), (2,1)腐烂,剩余1个:(2,2)
第3分钟:(1,2)腐烂 (等等,没有(1,2)这个新鲜橘子,grid[1][2]=0是空格子!)
我搞错了!重新看网格:
[2,1,1] <- 坐标 (0,0)=2, (0,1)=1, (0,2)=1
[1,1,0] <- 坐标 (1,0)=1, (1,1)=1, (1,2)=0 (空!)
[0,1,1] <- 坐标 (2,0)=0, (2,1)=1, (2,2)=1
新鲜橘子:(0,1), (0,2), (1,0), (1,1), (2,1), (2,2) 共6个
扩散路径:
(0,0)[初始腐烂]
-> 第1分钟:(0,1), (1,0) 腐烂
-> 第2分钟:(0,2)[从0,1], (1,1)[从1,0], (2,1)[从1,0向下?不对,(1,0)下方是(2,0)=0]
重新分析相邻关系:
(0,0)的上下左右:(无上, 1,0下, 无左, 0,1右)
(0,1)的上下左右:(无上, 1,1下, 0,0左, 0,2右)
(1,0)的上下左右:(0,0上, 2,0下=0, 无左, 1,1右)
(1,1)的上下左右:(0,1上, 2,1下, 1,0左, 1,2右=0)
(2,1)的上下左右:(1,1上, 无下, 2,0左=0, 2,2右)
扩散:
初始:queue=[(0,0)], fresh=6
第1分钟:处理(0,0),扩散到(0,1)和(1,0),queue=[(0,1),(1,0)], fresh=4
第2分钟:处理(0,1),扩散到(0,2)和(1,1);处理(1,0),扩散到(1,1)(已在队列),queue=[(0,2),(1,1)], fresh=2
第3分钟:处理(0,2)无新扩散;处理(1,1),扩散到(2,1),queue=[(2,1)], fresh=1
第4分钟:处理(2,1),扩散到(2,2),queue=[(2,2)], fresh=0
结束,minutes=4
正确!
Python代码
from typing import List
from collections import deque
def orangesRotting(grid: List[List[int]]) -> int:
"""
解法一:多源BFS
思路:所有腐烂橘子作为起点,同时向外扩散,层数=分钟数
"""
rows, cols = len(grid), len(grid[0])
queue = deque()
fresh_count = 0
# 初始化:收集所有腐烂橘子和新鲜橘子数量
for i in range(rows):
for j in range(cols):
if grid[i][j] == 2:
queue.append((i, j)) # 腐烂橘子入队
elif grid[i][j] == 1:
fresh_count += 1 # 统计新鲜橘子
# 边界情况:没有新鲜橘子
if fresh_count == 0:
return 0
minutes = 0
directions = [(-1,0), (1,0), (0,-1), (0,1)] # 上下左右
# BFS分层遍历
while queue:
# 处理当前层(当前这一分钟的所有腐烂橘子)
size = len(queue)
for _ in range(size):
r, c = queue.popleft()
# 向四个方向扩散
for dr, dc in directions:
nr, nc = r + dr, c + dc
# 边界检查 + 新鲜橘子检查
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == 1:
grid[nr][nc] = 2 # 标记为腐烂
fresh_count -= 1 # 新鲜橘子-1
queue.append((nr, nc)) # 下一分钟要处理
# 本层处理完,时间+1(注意:只有队列非空才加时间)
if queue: # 关键:只有还有橘子要腐烂才计时
minutes += 1
# 检查是否还有新鲜橘子
return minutes if fresh_count == 0 else -1
# ✅ 测试
print(orangesRotting([
[2,1,1],
[1,1,0],
[0,1,1]
])) # 期望输出:4
print(orangesRotting([[2,1,1],[0,1,1],[1,0,1]])) # 期望输出:-1 (右下角孤立)
print(orangesRotting([[0,2]])) # 期望输出:0 (无新鲜橘子)
print(orangesRotting([[1]])) # 期望输出:-1 (无腐烂源头)
复杂度分析
- 时间复杂度:O(m × n) — 每个格子最多入队一次
- 初始化遍历:O(m × n)
- BFS遍历:每个橘子最多入队一次,O(m × n)
- 总计:O(m × n)
- 空间复杂度:O(m × n) — 队列最坏情况下存储所有橘子
- 极端情况:全是腐烂橘子,初始队列大小为 m × n
优缺点
- ✅ 时间最优,每个格子只访问一次
- ✅ 代码清晰,分层处理逻辑简单
- ✅ 支持多源同时扩散
- ⚠️ 修改了输入网格(可用 visited 数组避免)
🏆 解法二:原地标记 + 时间戳(最优解)
优化思路
解法一中,我们用队列的"分层"来区分不同分钟。实际上可以直接在网格中存储"腐烂时间",避免分层计数的复杂性。
💡 关键想法:用
grid[i][j]存储这个橘子在第几分钟腐烂(初始腐烂=2,第1分钟=3,第2分钟=4...),最后返回max(grid) - 2
图解过程
初始:
2 1 1 <- 2表示第0分钟已腐烂
1 1 0
0 1 1
第1分钟:
2 3 1 <- 3表示第1分钟腐烂
3 1 0
0 1 1
第2分钟:
2 3 4 <- 4表示第2分钟腐烂
3 4 0
0 4 1
第3分钟:
2 3 4
3 4 0
0 4 1 <- 注意:这里(2,2)=1无法被扩散到,因为周围没有新腐烂的
实际扩散应该是:
第1分钟:从2扩散,周围变为3
第2分钟:从3扩散,周围变为4
...
实际上这个优化并不明显,我们还是用标准的分层BFS更清晰。
Python代码
def orangesRotting_timestamp(grid: List[List[int]]) -> int:
"""
解法二:时间戳标记(变体)
思路:用数字表示腐烂时间,最后取最大值
"""
rows, cols = len(grid), len(grid[0])
queue = deque()
# 收集初始腐烂橘子
for i in range(rows):
for j in range(cols):
if grid[i][j] == 2:
queue.append((i, j, 0)) # (行,列,腐烂时间=0)
max_time = 0
directions = [(-1,0), (1,0), (0,-1), (0,1)]
while queue:
r, c, time = queue.popleft()
max_time = max(max_time, time)
for dr, dc in directions:
nr, nc = r + dr, c + dc
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == 1:
grid[nr][nc] = 2 # 标记为腐烂
queue.append((nr, nc, time + 1)) # 下一分钟腐烂
# 检查是否还有新鲜橘子
for row in grid:
if 1 in row:
return -1
return max_time
# ✅ 测试
print(orangesRotting_timestamp([
[2,1,1],
[1,1,0],
[0,1,1]
])) # 期望输出:4
复杂度分析
- 时间复杂度:O(m × n) — 相同
- 空间复杂度:O(m × n) — 队列大小
🐍 Pythonic 写法
利用生成器表达式统计新鲜橘子:
def orangesRotting_pythonic(grid: List[List[int]]) -> int:
"""简洁版:用生成器表达式和any()简化边界检查"""
from collections import deque
rows, cols = len(grid), len(grid[0])
queue = deque([(i, j) for i in range(rows) for j in range(cols) if grid[i][j] == 2])
fresh = sum(row.count(1) for row in grid)
if fresh == 0:
return 0
minutes = 0
directions = [(-1,0), (1,0), (0,-1), (0,1)]
while queue and fresh > 0:
for _ in range(len(queue)):
r, c = queue.popleft()
for dr, dc in directions:
nr, nc = r + dr, c + dc
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == 1:
grid[nr][nc] = 2
fresh -= 1
queue.append((nr, nc))
minutes += 1
return minutes - 1 if fresh == 0 else -1 # 注意:最后一轮多加了1
注意:上面的写法有个坑 — minutes 在最后一轮队列为空时还会 +1,所以需要 minutes - 1。实际面试中建议用标准版本,避免边界处理错误。
⚠️ 面试建议:先写清晰版本展示思路,再提 Pythonic 写法展示语言功底。 面试官更看重你的思考过程,而非代码行数。
📊 解法对比
| 维度 | 🏆 解法一:多源BFS(最优) | 解法二:时间戳标记 |
|---|---|---|
| 时间复杂度 | O(m × n) | O(m × n) |
| 空间复杂度 | O(m × n) | O(m × n) |
| 代码难度 | 简单 | 简单 |
| 面试推荐 | ⭐⭐⭐ ← 首选 | ⭐⭐ |
| 适用场景 | 通用,清晰 | 代码稍简洁 |
为什么解法一是最优解:
- 时间复杂度已达最优 O(m × n)(至少要遍历所有格子)
- 分层逻辑清晰,用
size = len(queue)明确区分每一分钟 - 代码健壮,边界处理简单(队列为空自动结束)
面试建议:
- 先口述暴力法思路(每分钟全网格扫描),表明理解题意
- 立即优化到🏆最优解(多源BFS),强调"所有腐烂橘子同时扩散"
- 重点讲解:"初始时将所有腐烂橘子加入队列,BFS分层遍历,每层代表一分钟"
- 手动测试边界用例(无新鲜橘子/无腐烂源头/有隔离)
- 强调为什么是多源BFS而非单源:因为有多个初始腐烂橘子,它们要"同时"向外扩散
🎤 面试现场
模拟面试中的完整对话流程,帮你练习"边想边说"。
面试官:请你解决一下这道腐烂的橘子的题目。
你:(审题30秒)好的,这道题要求计算所有新鲜橘子腐烂所需的最小分钟数,腐烂是同时向四周扩散的。让我先想一下...
我的第一个想法是每分钟遍历整个网格,找到所有腐烂橘子,将它们周围的新鲜橘子标记为腐烂,重复直到没有变化。但这样时间复杂度是 O((m×n)²),因为每分钟都要全网格扫描。
优化方法是用 多源BFS:初始时将所有腐烂橘子加入队列,然后逐层扩散,每层代表一分钟。这样时间复杂度优化到 O(m×n)。
面试官:很好,请写一下代码。
你:(边写边说)我的步骤是:
- 初始化:遍历网格,收集所有腐烂橘子
2的坐标,加入队列;同时统计新鲜橘子1的数量 - BFS分层遍历:每次处理完队列中的一层(用
size = len(queue)记录本层元素数),时间 +1 - 向四个方向扩散:若相邻格子是新鲜橘子
1,标记为2,新鲜计数 -1,加入队列 - 最后检查:若
fresh_count > 0,说明有橘子无法被腐烂,返回-1;否则返回minutes
关键点是用 size = len(queue) 明确区分每一分钟的扩散范围。
面试官:测试一下?
你:用示例 [[2,1,1],[0,1,1],[1,0,1]] 走一遍:
- 初始:队列
[(0,0)],新鲜橘子5个 - 第1分钟:从
(0,0)扩散到(0,1),队列[(0,1)],新鲜4个 - 第2分钟:从
(0,1)扩散到(0,2)和(1,1),队列[(0,2), (1,1)],新鲜2个 - 第3分钟:从
(1,1)扩散到(2,1),队列[(2,1)],新鲜1个 - 第4分钟:从
(2,1)无法扩散到(2,2)(中间隔了(2,0)=0空格子) - 队列为空,
fresh_count = 1 > 0,返回-1(正确,右下角橘子被隔离)
再测一个边界:全是新鲜橘子 [[1,1]],初始队列为空,直接返回 -1(正确)。
高频追问
| 追问 | 应答策略 |
|---|---|
| "如果腐烂速度不同呢?" | 可以在队列中存储 (r, c, speed),扩散时根据速度调整时间,用优先队列维护最早腐烂时间 |
| "如果要返回具体的扩散路径?" | 在BFS时记录每个格子的前驱节点,最后回溯路径 |
| "空间复杂度能优化到O(1)吗?" | 不能,BFS必须用队列存储待访问节点,最坏情况队列大小为 O(m×n) |
| "如果网格非常大怎么办?" | 可以只存储边界橘子坐标,而非所有腐烂橘子;或用并行BFS分块处理 |
🎓 知识点总结
Python技巧卡片 🐍
# 技巧1:列表推导式收集坐标
queue = deque([(i, j) for i in range(rows) for j in range(cols) if grid[i][j] == 2])
# 技巧2:sum() + 生成器统计元素
fresh_count = sum(row.count(1) for row in grid)
# 技巧3:BFS分层遍历模板
while queue:
size = len(queue) # 记录本层元素数
for _ in range(size):
node = queue.popleft()
# 处理node...
level += 1 # 本层处理完,层数+1
💡 底层原理(选读)
为什么用多源BFS而非多次单源BFS?
单源BFS:从每个腐烂橘子单独BFS,取最大时间 → 时间复杂度 O(k × m × n),k是腐烂橘子数
多源BFS:将所有腐烂橘子同时加入队列,一次BFS → 时间复杂度 O(m × n)
核心区别:多源BFS利用了"同时扩散"的特性,避免重复访问。
BFS分层的本质是什么?
BFS天然按"距离"分层,第 k 层的所有节点距离起点都是 k 步。
对于本题:
- 第0层:初始腐烂橘子(距离=0分钟)
- 第1层:第1分钟腐烂的橘子(距离初始腐烂橘子1步)
- 第k层:第k分钟腐烂的橘子
用
size = len(queue)记录每层元素数,确保按层处理。
算法模式卡片 📐
- 模式名称:多源BFS(Multi-Source BFS)
- 适用条件:多个起点同时向外扩散,求最短路径/最少步数/覆盖时间
- 识别关键词:"同时扩散""多个起点""最少步数""感染传播"
- 模板代码:
def multi_source_bfs(grid, sources):
"""多源BFS模板:从多个起点同时扩散"""
from collections import deque
queue = deque(sources) # 初始:所有起点入队
visited = set(sources)
level = 0
while queue:
size = len(queue)
for _ in range(size):
node = queue.popleft()
# 处理node...
for neighbor in get_neighbors(node):
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
level += 1
return level
易错点 ⚠️
-
分层计时错误
- ❌ 错误:每次
popleft()就minutes += 1 - ✅ 正确:处理完整层后才
minutes += 1,用size = len(queue)控制
- ❌ 错误:每次
-
忘记检查剩余新鲜橘子
- ❌ 错误:直接返回
minutes,忽略孤立橘子 - ✅ 正确:最后检查
fresh_count == 0,否则返回-1
- ❌ 错误:直接返回
-
边界条件:无新鲜橘子
- ❌ 错误:返回
minutes(可能是正数) - ✅ 正确:初始检查
if fresh_count == 0: return 0
- ❌ 错误:返回
🏗️ 工程实战(选读)
这个算法思想在真实项目中的应用,让你知道"学了有什么用"。
- 场景1:病毒传播模拟:流行病学中模拟传染病扩散,多个初始感染者同时传播,计算多久能感染全部人群
- 场景2:网络故障扩散:数据中心中某些服务器故障,故障会扩散到相邻服务器,计算多久整个集群瘫痪
- 场景3:森林火灾模拟:多个起火点同时向周围扩散,计算整片森林烧毁所需时间
- 场景4:游戏AI寻路:多个敌人同时向玩家移动,计算最快被包围的时间
🏋️ 举一反三
完成本课后,试试这些同类题目来巩固知识:
| 题目 | 难度 | 相关知识点 | 提示 |
|---|---|---|---|
| LeetCode 1162. 地图分析 | Medium | 多源BFS | 从所有陆地出发,求离陆地最远的海洋 |
| LeetCode 542. 01矩阵 | Medium | 多源BFS | 从所有0出发,求每个1到最近0的距离 |
| LeetCode 1091. 二进制矩阵最短路径 | Medium | BFS最短路径 | 从(0,0)到(n-1,n-1)的最短路径 |
| LeetCode 1926. 迷宫中离入口最近的出口 | Medium | BFS最短路径 | 从入口到任意出口的最短步数 |
| LeetCode 2258. 逃离火灾 | Hard | 多源BFS + 二分 | 人和火同时扩散,求能到达安全屋的最晚出发时间 |
📝 课后小测
试试这道变体题,不要看答案,自己先想5分钟!
题目:如果腐烂橘子每分钟可以向八个方向(包含对角线)扩散,如何修改算法?时间复杂度会变化吗?
💡 提示(实在想不出来再点开)
只需修改方向数组,从4个方向改为8个方向,其他逻辑完全相同。
✅ 参考答案
def orangesRotting_8_directions(grid: List[List[int]]) -> int:
"""
变体:八方向扩散
思路:只需修改directions数组,其他不变
"""
from collections import deque
rows, cols = len(grid), len(grid[0])
queue = deque()
fresh_count = 0
for i in range(rows):
for j in range(cols):
if grid[i][j] == 2:
queue.append((i, j))
elif grid[i][j] == 1:
fresh_count += 1
if fresh_count == 0:
return 0
minutes = 0
# 八个方向:上下左右 + 四个对角
directions = [
(-1,0), (1,0), (0,-1), (0,1), # 上下左右
(-1,-1), (-1,1), (1,-1), (1,1) # 左上、右上、左下、右下
]
while queue:
size = len(queue)
for _ in range(size):
r, c = queue.popleft()
for dr, dc in directions:
nr, nc = r + dr, c + dc
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == 1:
grid[nr][nc] = 2
fresh_count -= 1
queue.append((nr, nc))
if queue:
minutes += 1
return minutes if fresh_count == 0 else -1
时间复杂度:仍然是 O(m × n),因为每个格子最多入队一次,虽然方向数增加,但不影响渐进复杂度。
示例:
输入: [[2,1,1],
[0,0,0],
[1,1,1]]
四方向:永远无法腐烂下方橘子(中间隔了空行),返回-1
八方向:第1分钟从(0,0)对角扩散到(1,1),再扩散到下方,可以全部腐烂
如果这篇内容对你有帮助,推荐收藏 AI Compass:github.com/tingaicompa… 更多系统化题解、编程基础和 AI 学习资料都在这里,后续复习和拓展会更省时间。