跳到正文
第 994 题中等

腐烂的橘子

Rotting Oranges

每分钟,腐烂橘子会感染上下左右相邻的新鲜橘子;返回全部腐烂的最短分钟数,做不到返回 -1。

在力扣中练习

题目描述

在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一:

  • 值 0 代表空单元格;
  • 值 1 代表新鲜橘子;
  • 值 2 代表腐烂的橘子。

每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。

返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1 。

 

示例 1:

输入:grid = [[2,1,1],[1,1,0],[0,1,1]]
输出:4

示例 2:

输入:grid = [[2,1,1],[0,1,1],[1,0,1]]
输出:-1
解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个方向上。

示例 3:

输入:grid = [[0,2]]
输出:0
解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0 。

 

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 10
  • grid[i][j] 仅为 0、1 或 2

解题思路

主解法

多源 BFS · 分层扩散

所有腐烂橘子同时入队,一轮就是一分钟

如果只有一个腐烂橘子,从它出发做 BFS,层数就是分钟数。现在可能有多个腐烂源,而且它们同时扩散,所以要把所有腐烂橘子一起放进队列,做多源 BFS。

每一轮先锁定当前队列大小 sz,只处理这 sz 个节点;它们感染出的新橘子留到下一轮。这样“轮”就对应“分钟”,同一分钟内不会出现连锁感染。

分步图解

所有腐烂橘子一起入队;先统计新鲜橘子数量,最后用它判断是否有橘子永远无法腐烂。

第 0 分钟 · 橙色是腐烂橘子
0
1
2
3
0
2
1
·
1
1
1
1
·
·
2
·
1
1
1
3
1
1
1
2
每一分钟,所有已腐烂橘子同时向上下左右感染;空格不可通行。
本层待处理的腐烂橘子先进先出
← 队首出队队尾入队 ←
0,0front
3,3rear
剩余新鲜橘子
10
1 / 3
所有腐烂橘子同时扩散,同一分钟内的新感染源下一分钟才继续向外。

感染时立刻把格子改成 2 并让 fresh 减一,避免同一个新鲜橘子被多个源重复入队。BFS 结束后如果 fresh 仍大于 0,说明有橘子被空格隔开,返回 -1。

分步图解

第 2 分钟:新感染 (1,1)、(2,2)、(3,1);剩余新鲜 3 个。

minutes
2
第 2 分钟 · 橙色是腐烂橘子
0
1
2
3
0
2
2
·
1
1
2
2
·
·
2
·
1
2
2
3
1
2
2
2
每一分钟,所有已腐烂橘子同时向上下左右感染;空格不可通行。
本层待处理的腐烂橘子先进先出
← 队首出队队尾入队 ←
1,1front
2,2
3,1rear
新入队 1,1、2,2、3,1
剩余新鲜橘子
3
1 / 3
扩散停止后仍剩新鲜橘子,说明它被空格隔开,返回 -1 而不是分钟数。

自拟网格有两个腐烂源和一个被空格包围的新鲜橘子。前三分钟能感染所有可达橘子,但右上角那个永远不变,最终返回 -1,而不是 3。

时间 O(m·n)空间 O(m·n)

易错点

  • 只从一个腐烂橘子出发:其他源的最短时间被忽略。
  • 不分层,直接一个 while 处理队列:分钟数无法对应层。
  • 感染后不立刻标记:同一分钟可能被多个邻居重复入队。
  • 只返回 minutes,不检查 fresh:不可达时应该返回 -1。

代码与演示

演示输入grid = [[2,1,0,1],[1,1,0,0],[0,1,1,1],[1,1,1,2]]

所有腐烂橘子一起入队;先统计新鲜橘子数量,最后用它判断是否有橘子永远无法腐烂。

第 0 分钟 · 橙色是腐烂橘子
0
1
2
3
0
2
1
·
1
1
1
1
·
·
2
·
1
1
1
3
1
1
1
2
每一分钟,所有已腐烂橘子同时向上下左右感染;空格不可通行。
本层待处理的腐烂橘子先进先出
← 队首出队队尾入队 ←
0,0front
3,3rear
剩余新鲜橘子
10
1 / 5
查看 C++ 代码

延伸阅读

  • 多源 BFS 的“源”可以看成超级源点的第一层;所有源同时入队,层数仍然是最短距离。
  • 网格 BFS 要区分“已访问”和“可通行”:这里腐烂状态本身就是访问标记。