题目
在给定的网格中,每个单元格可以有以下三个值之一:
- 值 0 代表空单元格;
- 值 1 代表新鲜橘子;
- 值 2 代表腐烂的橘子。
每分钟,任何与腐烂的橘子(在 4 个正方向上)相邻的新鲜橘子都会腐烂。
返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1。
示例 1:
输入:[[2,1,1],[1,1,0],[0,1,1]]
输出:4
示例 2:
输入:[[2,1,1],[0,1,1],[1,0,1]]
输出:-1
解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个正向上。
示例 3:
输入:[[0,2]]
输出:0
解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0 。
提示:
1 <= grid.length <= 10
1 <= grid[0].length <= 10
grid[i][j] 仅为 0、1 或 2
解题思路
由题目我们可以知道每分钟每个腐烂的橘子都会使上下左右相邻的新鲜橘子腐烂,这其实是一个模拟广度优先搜索的过程。所谓广度优先搜索,就是从起点出发,每次都尝试访问同一层的节点,如果同一层都访问完了,再访问下一层,最后广度优先搜索找到的路径就是从起点开始的最短合法路径。
BFS 可以看成是层序遍历。从某个结点出发,BFS 首先遍历到距离为 1 的结点,然后是距离为 2、3、4…… 的结点。因此,BFS 可以用来求最短路径问题。BFS 先搜索到的结点,一定是距离最近的结点。
如何写(最短路径的) BFS 代码
我们都知道 BFS 需要使用队列,代码框架是这样子的(伪代码):
while queue 非空:
node = queue.pop()
for node 的所有相邻结点 m:
if m 未访问过:
queue.push(m)
但是用 BFS 来求最短路径的话,这个队列中第 1 层和第 2 层的结点会紧挨在一起,无法区分。因此,我们需要稍微修改一下代码,在每一层遍历开始前,记录队列中的结点数量 n ,然后一口气处理完这一层的 n 个结点。代码框架是这样的:
depth = 0 # 记录遍历到第几层
while queue 非空:
depth++
n = queue 中的元素个数
循环 n 次:
node = queue.pop()
for node 的所有相邻结点 m:
if m 未访问过:
queue.push(m)
本题步骤:
1)记录行数与列数,初始化队列、时间;
2)找出初始时就已经腐烂了的橘子,将它们入队;
3)当队列不为空时进行循环,执行BFS遍历,找出每个烂橘子上下左右相邻的新鲜橘子,这里需判断边界情况,并将它们依次入队,时间加1;
4)由于可能存在无法被污染的橘子,我们需要记录新鲜橘子的数量。在 BFS 中,每遍历到一个橘子(污染了一个橘子),就将新鲜橘子的数量减一。如果 BFS 结束后这个数量仍未减为零,说明存在无法被污染的橘子。
代码
class Solution:
def orangesRotting(self, grid: List[List[int]]) -> int:
# 初始化
rows = len(grid)
cols = len(grid[0])
time = 0
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] #上、下、左、右
queue = []
# 找出一开始就已经腐烂了的橘子
for i in range(rows):
for j in range(cols):
if grid[i][j] == 2:
queue.append([i,j,time])
# BFS遍历
while queue:
i, j, time = queue.pop(0)
for di, dj in directions:
if 0 <= i + di < rows and 0 <= j + dj < cols and grid[i + di][j + dj] == 1:
grid[i + di][j + dj] = 2
queue.append([i + di, j + dj, time + 1])
# 判断是否存在无法被污染的橘子
for row in grid:
if 1 in row:
return -1
return time