LeetCode 994. 腐烂的橘子
题目描述


题意分析
网格中 0 是空格,1 是新鲜橘子,2 是腐烂橘子。每分钟,所有腐烂橘子同时使上下左右相邻的新鲜橘子腐烂。求不再有新鲜橘子所需的最少分钟数;无法全部感染返回 -1,初始没有新鲜橘子返回 0。
解法:多源 BFS 分层扩散
核心思路
[!blue]
把橘子看成节点,相邻橘子之间连一条边,每经过一条边需要一分钟。一个新鲜橘子最早腐烂的时间,就是它到任意初始腐烂橘子的最短距离。所有初始腐烂橘子同时入队作为第 0 层,多源 BFS 按距离递增访问,第一次感染便是最早可能的时间。
每轮开始固定队列长度
size,只处理这一层。处理过程中感染的新橘子加到队尾,留到下一分钟继续传播;整层处理完才增加minutes。这样一次循环恰好表示经过一分钟,最后一批新鲜橘子被感染时的分钟数就是答案。用
fresh记录剩余新鲜橘子的数量。发现新鲜邻居时立即改为 2、减少fresh再入队,既标记已访问,也避免多个来源重复感染同一格。循环同时要求fresh > 0,因此全部感染后不会再多处理一层;若队列耗尽仍有新鲜橘子,说明它们不可达。
解题步骤
- 扫描网格:新鲜橘子计入
fresh,所有腐烂橘子加入队列。- 当队列非空且
fresh > 0时,记录当前队列长度size,它就是本分钟参与扩散的橘子数。- 弹出恰好
size个格子,检查四个方向;越界、空格和已腐烂格都跳过,新鲜邻居立即标记为 2、减少fresh并入队。- 当前层处理完后令
minutes++。- 循环结束后,若
fresh == 0返回分钟数,否则返回 -1。
代码实现
class Solution {
public int orangesRotting(int[][] grid) {
int rows = grid.length;
int cols = grid[0].length;
Queue<int[]> queue = new ArrayDeque<>();
int fresh = 0;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
if (grid[r][c] == 1) {
fresh++;
} else if (grid[r][c] == 2) {
queue.offer(new int[] {
r,
c
});
}
}
}
int minutes = 0;
int[][] dirs = {
{1, 0},
{-1, 0},
{0, 1},
{0, -1},
};
while (!queue.isEmpty() && fresh > 0) {
// 同一层同时扩散,新感染的橘子留到下一分钟。
int size = queue.size();
for (int i = 0; i < size; i++) {
int[] cur = queue.poll();
for (int[] dir : dirs) {
int nr = cur[0] + dir[0];
int nc = cur[1] + dir[1];
if (nr < 0 || nr >= rows || nc < 0 || nc >= cols || grid[nr][nc] != 1) {
continue;
}
// 入队前立即感染并减少鲜橘子,其他邻居就不会重复处理。
grid[nr][nc] = 2;
fresh--;
queue.offer(new int[] {
nr,
nc
});
}
}
minutes++;
}
return fresh == 0 ? minutes : -1;
}
}
func orangesRotting(grid [][]int) int {
rows := len(grid)
cols := len(grid[0])
queue := make([][2]int, 0)
fresh := 0
for r := 0; r < rows; r++ {
for c := 0; c < cols; c++ {
if grid[r][c] == 1 {
fresh++
} else if grid[r][c] == 2 {
queue = append(queue, [2]int{
r,
c,
})
}
}
}
dirs := [][]int{
{1, 0},
{-1, 0},
{0, 1},
{0, -1},
}
minutes := 0
for len(queue) > 0 && fresh > 0 {
// 同一层同时扩散,新感染的橘子留到下一分钟。
size := len(queue)
for i := 0; i < size; i++ {
cur := queue[0]
queue = queue[1:]
for _, dir := range dirs {
nr := cur[0] + dir[0]
nc := cur[1] + dir[1]
if nr < 0 || nr >= rows || nc < 0 || nc >= cols || grid[nr][nc] != 1 {
continue
}
// 入队前立即感染并减少鲜橘子,其他邻居就不会重复处理。
grid[nr][nc] = 2
fresh--
queue = append(queue, [2]int{
nr,
nc,
})
}
}
minutes++
}
if fresh == 0 {
return minutes
}
return -1
}
复杂度分析
- 时间复杂度:$O(mn)$,其中
m、n为网格行列数。初始化扫描所有格子,每个橘子至多入队一次,出队后只检查四个方向。- 空间复杂度:$O(mn)$,最坏情况下队列可同时保存整张网格中的格子。
关键点总结
[!green]
- 同时从所有腐烂橘子出发,BFS 才能求出每个新鲜橘子到最近感染源的距离。
- 固定每层大小模拟同时传播,最后完成感染的那一层决定总时间。
- 原地把 1 改成 2 可同时表示感染和访问,无需额外
visited。
易错点总结
[!yellow]
- 初始没有新鲜橘子时直接得到 0;有新鲜橘子但没有腐烂橘子时,队列为空,返回 -1。
- 不固定
size会在同一轮继续处理新入队节点,把多分钟传播压成一分钟。- 循环不加
fresh > 0,可能在全部感染后多处理最后一层,分钟数多 1。- 感染后不立即改成 2,会被其他相邻橘子重复入队并重复减少
fresh。- 空格不能传播感染。BFS 结束后必须检查
fresh,队列为空也可能是剩余新鲜橘子被空格隔开。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 542. 01 矩阵 | 中等 | 同样从多个源同时BFS,本题最终取感染时间最大值并检测是否仍有不可达目标。 |
| 286. 墙与门 | 中等 | 同样在障碍网格上传播最近源距离,本题源是所有初始腐烂橘子。 |
| 1162. 地图分析 | 中等 | 从多个起点同时进行逐层广度优先搜索;本题模拟腐烂波及相邻橘子的分钟数,该题从全部陆地求海洋的最近距离。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!