leetcode
leetcode 901 ~ 950
腐烂的橘子

腐烂的橘子

难度:

标签:

题目描述

在给定的 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] 仅为 01 或 2

代码结果

运行时间: 18 ms, 内存: 16.1 MB


/*
 * 思路:
 * 1. 首先统计所有新鲜橘子的数量,并将所有腐烂橘子的坐标放入队列。
 * 2. 使用广度优先搜索(BFS)算法,每次从队列中取出一个腐烂的橘子,将它周围的新鲜橘子腐烂。
 * 3. 每腐烂一层新鲜橘子,时间加1,直到没有新鲜橘子。
 * 4. 最后,检查是否还有新鲜橘子,若有返回-1,否则返回时间。
 */
import java.util.*;
import java.util.stream.*;

public class Solution {
    public int orangesRotting(int[][] grid) {
        int rows = grid.length;
        int cols = grid[0].length;
        int freshOranges = 0;
        Queue<int[]> rottenQueue = new LinkedList<>();
        for (int r = 0; r < rows; r++) {
            for (int c = 0; c < cols; c++) {
                if (grid[r][c] == 1) {
                    freshOranges++;
                } else if (grid[r][c] == 2) {
                    rottenQueue.offer(new int[]{r, c});
                }
            }
        }
        if (freshOranges == 0) return 0;
        int minutes = 0;
        int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
        while (!rottenQueue.isEmpty()) {
            int size = rottenQueue.size();
            List<int[]> newRotten = IntStream.range(0, size).mapToObj(i -> rottenQueue.poll()).flatMap(point -> Arrays.stream(directions).map(dir -> new int[]{point[0] + dir[0], point[1] + dir[1]}).filter(xy -> xy[0] >= 0 && xy[0] < rows && xy[1] >= 0 && xy[1] < cols && grid[xy[0]][xy[1]] == 1).peek(xy -> { grid[xy[0]][xy[1]] = 2; freshOranges--; })).collect(Collectors.toList());
            rottenQueue.addAll(newRotten);
            if (!rottenQueue.isEmpty()) minutes++;
        }
        return freshOranges == 0 ? minutes : -1;
    }
}

解释

方法:

This solution uses the Breadth-First Search (BFS) algorithm to simulate the rotting process of oranges. It starts by identifying all initially rotten oranges and counting fresh oranges. It enqueues all rotten oranges' positions. Using BFS, it iteratively infects fresh oranges adjacent to rotten ones each minute, marking them rotten and decreasing the fresh count. This continues until no fresh oranges are left or there are no more adjacent fresh oranges to infect, at which point the process stops. If any fresh oranges remain unreachable, it returns -1, otherwise, it returns the number of minutes passed.

时间复杂度:

O(M * N)

空间复杂度:

O(M * N)

代码细节讲解

🦆
在算法中,为何选择使用广度优先搜索(BFS)而不是深度优先搜索(DFS)来模拟橘子腐烂的过程?
BFS 是模拟橘子腐烂过程的理想选择,因为它可以确保以最快的速度同时向所有方向传播。这种方式确保每一分钟内所有已腐烂的橘子都能同时影响其相邻的新鲜橘子。相比之下,DFS 会沿一个可能的路径深入,可能导致某些橘子比其他路径上的橘子更晚腐烂,这不符合橘子腐烂的自然过程,即橘子的腐烂应该是同时发生的。
🦆
请问算法在处理边界条件时,如何确保不会访问到网格之外的位置?
算法中使用了一个名为 `valid` 的辅助函数来检查一个位置是否在网格内。这个函数检查给定的坐标 (x, y) 是否满足 0 <= x < M 和 0 <= y < N,其中 M 和 N 分别是网格的行数和列数。只有当这些条件都满足时,才会对该位置进行处理,从而确保算法不会尝试访问网格边界之外的位置。
🦆
为什么在每一轮中需要使用一个队列来存储即将腐烂的橘子的位置?
使用队列存储即将腐烂的橘子的位置是为了按照腐烂发生的顺序处理每个橘子。在BFS中,队列首先初始化为所有已经腐烂的橘子的位置。每一轮中,算法从队列中移除当前腐烂的橘子,并将其周围的新鲜橘子标记为腐烂并加入队列中。这样可以保证每一分钟所有可腐烂的橘子都被处理,从而模拟出腐烂的扩散过程。
🦆
在BFS执行过程中,如果某一分钟没有新的橘子腐烂,算法是否会提前终止,还是会继续执行直到遍历完所有橘子?
在BFS执行过程中,如果某一轮结束时没有新的橘子腐烂(即队列为空),算法会立即终止。这是因为没有更多的腐烂橘子可以传播腐烂,所有剩余的新鲜橘子都不可达,因此进一步的搜索是无效的。算法会返回-1表示不可能腐烂所有橘子。

相关问题

墙与门

墙与门