Leetcode 200,994岛屿数量,腐烂的橘子

1.题目描述

题目解答

这道题又涉及到了一个全新的知识点:图的深度优先搜索。

深度优先搜索的定义可以理解为:优先不停的向下层寻找,直到碰壁了之后再返回上层,之后如果可以的话就继续向下层搜索。正好实现这种搜索方式的方法就是递归。

所以这道题我们可以先以左上角的1为中心,当遇见了一个全新的1,就说明发现了一个全新的岛屿,将这个1变为已标记,计数器加一。之后对这个岛屿进行深度优先搜索,上下左右的1全部变为已标记,就说明是这个岛屿的一部分,直到边界条件终止。

之后从头开始一行一行进行遍历,如果沿途中发现了未被标记的1,就说明又有新大陆,如法炮制即可。

java 复制代码
class Solution {
    // 岛屿计数:每发现一块没访问过的陆地就 +1,再用 DFS 把整座岛"淹没"掉
    int count = 0;

    public int numIslands(char[][] grid) {
        // visited[i][j] = true 表示这个格子已经被访问过(属于某座已知岛屿)
        boolean[][] visited = new boolean[grid.length][grid[0].length];

        // 主循环:扫遍网格里每一个格子
        // 这里外层循环 i 用的是 grid[0].length(列数),内层 j 用的是 grid.length(行数),
        // 配合 grid[j][i] 的访问方式,相当于 j 当行号、i 当列号
        for (int i = 0; i < grid[0].length; i++) {
            for (int j = 0; j < grid.length; j++) {
                // 是海水、或者已经被访问过 → 跳过
                if (grid[j][i] == '0' || visited[j][i]) {
                    continue;
                }
                // 到这里:遇到一块没访问过的陆地 = 发现一座新岛
                visited[j][i] = true;       // ★ 先把起点标记掉
                count++;                     // 岛屿数 +1
                dfs(grid, visited, i, j);   // 用 DFS 把整座岛"淹没"(标记掉所有相连的陆地)
            }
        }
        return count;
    }

    /**
     * DFS 淹没函数:从坐标 (x, y) 出发(这里 x 当列、y 当行),
     * 把与它四连通的所有陆地标记为已访问
     */
    public void dfs(char[][] grid, boolean[][] visited, int x, int y) {
        // 4 个方向:右、下、左、上(用数组比写 4 个 if-else 干净)
        int[][] dir = {{0,1}, {1,0}, {-1,0}, {0,-1}};

        for (int i = 0; i < 4; i++) {
            int nextx = x + dir[i][0];   // 候选邻居的列号
            int nexty = y + dir[i][1];   // 候选邻居的行号

            // ★ 越界时用 continue,不是 return!
            // return 会让整个 DFS 直接结束,剩下的方向全部被放弃,整座岛会被"切散"
            if (nextx > grid[0].length - 1 || nexty > grid.length - 1
                    || nextx < 0 || nexty < 0) {
                continue;
            }

            // 是陆地 + 没访问过 → 标记后继续递归
            if (grid[nexty][nextx] == '1' && !visited[nexty][nextx]) {
                visited[nexty][nextx] = true;            // ★ 入栈前先标记,避免重复访问
                dfs(grid, visited, nextx, nexty);       // 递归,往邻居继续"淹"
            } else {
                // 是水、或者已经访问过 → 换下一个方向
                continue;
            }
        }
    }
}

2.题目描述

题目解答

这道题和上一道题是一种不同的解法,用到了广度优先搜索。

广度优先搜索可以理解为:尽量把当前所在层数的左右元素全部都遍历一遍,确认无误之后再进入下一层。

对应到这道题就是:先建立一个队列,然后由上至下扫描每一个格子,如果发现了格子中有腐烂的橘子,就将其入队。

然后对队列进行操作:记录当前队列长度的快照,之后依次将腐烂的句子上下左右的橘子进行感染。之后将原来的感染橘子出队,再将新感染的入队,直到队列为空或者新鲜橘子的数量为0终止循环。

java 复制代码
class Solution {
    public int orangesRotting(int[][] grid) {
        // 局部 record:只在这个方法里可见的坐标二元组
        // 访问器是方法:cur.x()、cur.y(),别忘了括号
        record P(int x, int y) {}

        // 4 个方向:右、下、左、上(列偏移, 行偏移)
        int[][] dir = {{0,1}, {1,0}, {0,-1}, {-1,0}};

        // BFS 队列:装的是"已腐烂、等待扩散"的橘子坐标
        Deque<P> queue = new ArrayDeque<>();

        int fresh = 0;   // 新鲜橘子计数,用来判断"是否全部烂光"
        int time = 0;    // 经过的分钟数

        // ===== 第一步:扫描全图,统计新鲜橘子 + 把腐烂橘子全部入队 =====
        // 注意:外层 i 遍历列(grid[0].length),内层 j 遍历行(grid.length),
        // 配合 grid[j][i] 的访问方式,相当于 j 当行号、i 当列号
        for (int i = 0; i < grid[0].length; i++) {
            for (int j = 0; j < grid.length; j++) {
                if (grid[j][i] == 1) {
                    fresh++;              // 是新鲜橘子,计数
                }
                if (grid[j][i] == 2) {
                    queue.offer(new P(i, j));  // 是腐烂橘子,入队当 BFS 起点
                }
            }
        }

        // ===== 第二步:按层 BFS,一圈圈向外扩散 =====
        // 两个条件同时满足才继续:
        //   1. 队列不空(还有橘子在等待扩散)
        //   2. fresh > 0(还有新鲜橘子没烂)
        // fresh == 0 时提前退出,避免 time 多加一轮
        while (!queue.isEmpty() && fresh > 0) {
            int size = queue.size();  // ★ 先抓快照:当前这一"层"有几个橘子要扩散

            for (int i = 0; i < size; i++) {     // 处理这一层的所有橘子
                P cur = queue.poll();              // 取出队头

                for (int k = 0; k < 4; k++) {   // 4 个方向逐个试
                    int nextx = cur.x() + dir[k][0];   // 候选格子的列号(用 k!不是 i)
                    int nexty = cur.y() + dir[k][1];   // 候选格子的行号

                    // 越界、空格子(0)、已腐烂(2) → 这个方向走不通,换下一个
                    // ★ 注意 grid[0].length 是列上限、grid.length 是行上限
                    if (nextx < 0 || nextx > grid[0].length - 1
                            || nexty < 0 || nexty > grid.length - 1
                            || grid[nexty][nextx] == 0
                            || grid[nexty][nextx] == 2) {
                        continue;
                    }

                    // 到这里:是新鲜橘子(1) → 被感染,变腐烂
                    grid[nexty][nextx] = 2;        // ★ 染色的同时就标记,天然防止重复入队
                    queue.offer(new P(nextx, nexty)); // 加入下一轮扩散
                    fresh--;                         // 新鲜橘子少一个
                }
            }
            time++;  // ★ 这一层全部扩散完,才算过去 1 分钟
        }

        // ===== 第三步:判断结果 =====
        // fresh > 0 说明有橘子永远烂不到(被空格围死),题目要求返回 -1
        if (fresh > 0) {
            return -1;
        }
        return time;  // 全部烂光,返回总分钟数
    }
}
相关推荐
xzal126 小时前
GESP Python笔记
笔记·python·算法
wzdark6 小时前
算法中的记忆化思想与重复子问题优化4
算法
Discipline10297 小时前
流水线运行是否正随时间变得更加集中?
算法
Logic1017 小时前
C语言/数据结构位运算题解:异或XOR找出书柜中编号只出现一次的那一层——只出现一次的数字
c语言·数据结构·数组·位运算·时间复杂度·算法题·异或性质
lzhdim7 小时前
C#不为人知的10个魔法特性:资深开发者也会震惊的底层奥秘
java·前端·javascript·算法·c#
weixin_307779137 小时前
C++代码实现MATLAB中的pade函数功能
开发语言·c++·算法·matlab
smj2302_796826527 小时前
解决leetcode第4064题至多一次取反能被k整除的最长子数组II
python·算法·leetcode
AIGC小尼7 小时前
ACE-Step-1.5 本地 AI 音乐模型实战|text2music 歌词转人声,部署脚本、参数调优与排坑全记录
人工智能·算法·comfyui·ai漫剧
YOLO数据集集合7 小时前
树木检测数据集 | 树木检测 树种分类 航拍林业 森林监测9131期
人工智能·算法·机器学习·分类·数据挖掘·林业·树种分类
青山木7 小时前
Hot 100 --- 下一个排列
java·数据结构·算法·leetcode