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;  // 全部烂光,返回总分钟数
    }
}
相关推荐
kiracrimson2 小时前
【无标题】
数据结构
hansang_IR3 小时前
【题解】P5364 [SNOI2017] 礼物
c++·线性代数·算法
Tisfy3 小时前
LeetCode 3871.统计范围内的逗号 II:每次算一个逗号
数学·算法·leetcode·题解
小O的算法实验室3 小时前
IEEE TEVC,基于K-means与DQN辅助元启发式算法的多目标两栖无人艇调度模型
算法·kmeans·启发式算法
zcmodeltech3 小时前
冷链物流沙盘模型控制系统设计——基于STM32与Modbus RTU的冷藏车、冷库全流程动态展示方案
数据结构·stm32·单片机·嵌入式硬件·物联网·能源
mmmmath_33 小时前
LeetCode.350.两个数组的交集II
算法
Ivanqhz3 小时前
图是“拓扑 + 类型 + 形状“的世界
算法·决策树·机器学习·php·集成学习
find1star3 小时前
LeetCode 54:螺旋矩阵——用四个边界模拟矩阵收缩
java·算法·leetcode·边缘计算·学习方法
z_mazin4 小时前
逆向一种私有二进制序列化格式:从零到字节级解析器
网络·python·网络协议·算法·rpc