LeetCode算法题(Go语言实现)_48
题目
在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一:
值 0 代表空单元格;
值 1 代表新鲜橘子;
值 2 代表腐烂的橘子。
每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。
返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1 。
一、代码实现(多源BFS)
func orangesRotting(grid [][]int) int {m, n := len(grid), len(grid[0])queue := [][2]int{}fresh := 0// 初始化队列并统计新鲜橘子数量for i := 0; i < m; i++ {for j := 0; j < n; j++ {if grid[i][j] == 2 {queue = append(queue, [2]int{i, j})} else if grid[i][j] == 1 {fresh++}}}if fresh == 0 {return 0}dirs := [4][2]int{{-1,0}, {1,0}, {0,-1}, {0,1}}time := 0for len(queue) > 0 {levelSize := len(queue)levelSpread := falsefor i := 0; i < levelSize; i++ {cell := queue[0]queue = queue[1:]for _, d := range dirs {x, y := cell[0]+d[0], cell[1]+d[1]if x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 {grid[x][y] = 2fresh--queue = append(queue, [2]int{x, y})levelSpread = true}}}if levelSpread {time++}}if fresh > 0 {return -1}return time
}
二、算法分析
1. 核心思路
- 多源广度优先搜索:初始将所有腐烂橘子入队,同时进行层级扩散
- 层级时间统计:每完成一轮队列处理代表经过1分钟
- 新鲜橘子计数:实时更新剩余新鲜橘子数量,判断终止条件
2. 关键步骤
-
初始化阶段:
- 遍历网格收集腐烂橘子坐标
- 统计新鲜橘子总数
fresh
- 特判无新鲜橘子时直接返回0
-
BFS扩散过程:
- 每次处理当前队列的所有节点(对应同一时间层级)
- 四个方向扩散,感染相邻新鲜橘子
- 维护
levelSpread
标志判断是否实际发生扩散
-
终止条件判断:
- 最终检查
fresh
是否为0 - 剩余新鲜橘子说明存在隔离区域
- 最终检查
3. 复杂度
指标 | 值 | 说明 |
---|---|---|
时间复杂度 | O(m*n) | 每个节点最多入队一次 |
空间复杂度 | O(m*n) | 队列最大存储所有腐烂橘子坐标 |
三、图解示例
四、边界条件与扩展
1. 特殊场景验证
- 全腐烂初始状态:返回0(如示例3)
- 隔离区域存在:返回-1(如示例2的角落橘子)
- 多源同时扩散:不同腐烂源并行加速感染
2. 扩展应用
- 动态障碍物:支持实时更新墙的位置重新计算
- 三维空间扩展:增加z轴方向扩散维度
- 感染概率模型:每次感染存在成功概率
3. 多语言实现
import java.util.LinkedList;
import java.util.Queue;public class Solution {public int orangesRotting(int[][] grid) {if (grid == null || grid.length == 0) {return -1;}int rows = grid.length;int cols = grid[0].length;int fresh = 0;Queue<int[]> queue = new LinkedList<>();// 初始化队列和新鲜橘子计数for (int r = 0; r < rows; r++) {for (int c = 0; c < cols; c++) {if (grid[r][c] == 2) {queue.offer(new int[]{r, c});} else if (grid[r][c] == 1) {fresh++;}}}// 如果没有新鲜橘子,直接返回0if (fresh == 0) {return 0;}int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};int minutes = 0;while (!queue.isEmpty() && fresh > 0) {int levelSize = queue.size();for (int i = 0; i < levelSize; i++) {int[] current = queue.poll();for (int[] dir : directions) {int nr = current[0] + dir[0];int nc = current[1] + dir[1];if (nr >= 0 && nr < rows && nc >= 0 && nc < cols && grid[nr][nc] == 1) {grid[nr][nc] = 2;fresh--;queue.offer(new int[]{nr, nc});}}}if (!queue.isEmpty()) {minutes++;}}return fresh == 0 ? minutes : -1;}
}
from collections import dequedef orangesRotting(grid):if not grid:return -1rows = len(grid)cols = len(grid[0])fresh = 0queue = deque()# 初始化队列和新鲜橘子计数for r in range(rows):for c in range(cols):if grid[r][c] == 2:queue.append((r, c))elif grid[r][c] == 1:fresh += 1# 如果没有新鲜橘子,直接返回0if fresh == 0:return 0directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]minutes = 0while queue and fresh > 0:# 处理当前层的所有橘子level_size = len(queue)for _ in range(level_size):r, c = queue.popleft()for dr, dc in directions:nr, nc = r + dr, c + dcif 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == 1:grid[nr][nc] = 2fresh -= 1queue.append((nr, nc))if queue: # 只有这一层有橘子腐烂时才增加时间minutes += 1return minutes if fresh == 0 else -1
五、总结与优化
1. 算法对比
方法 | 优势 | 适用场景 |
---|---|---|
BFS | 保证最短时间 | 常规网格扩散问题 |
DFS | 空间效率高 | 路径存在性验证 |
并查集 | 动态连接关系维护 | 需要持续更新感染关系 |
2. 工程优化
- 层级标记优化:使用轮次标记替代队列长度计算
- 内存压缩存储:用位运算记录感染状态
- 并行计算:多线程处理不同腐烂源扩散
3. 扩展方向
- 权重扩散:不同方向感染速度不同
- 抗感染机制:部分橘子具有抗感染能力
- 可视化模拟:生成感染过程动画演示