day49 代码随想录算法训练营 图论专题2

1 今日打卡

岛屿数量深度优先搜索版 99. 计数孤岛

岛屿数量广度优先搜索版 99. 计数孤岛

岛屿的最大面积 100. 最大岛屿的面积

2 岛屿数量深度优先搜索

2.1 思路

初始化与输入处理:读取网格的行数 n、列数 m,构建二维网格数组map存储网格数据,同时创建visited布尔数组标记每个位置是否被访问过(避免重复统计);

遍历网格找连通块起点:逐行逐列遍历网格的每个位置,若当前位置未被访问且值为 1 ,说明发现一个新的连通块,计数res加 1,先标记该位置为已访问,再调用 DFS 遍历这个连通块的所有关联位置;

DFS 递归遍历连通区域:从起点出发,向上下左右四个方向遍历相邻位置,对每个相邻位置先做边界校验 (避免越界),再判断是否未被访问且值为 1,满足则标记为已访问并递归遍历该位置的相邻区域,直到遍历完整个连通块的所有 1。

2.2 实现代码

java 复制代码
import java.util.*;
import java.lang.*;

public class Main {
    // 定义上下左右四个方向的偏移量(行、列):右、左、下、上
    public static int[][] dir = {{0,1},{0,-1},{1,0},{-1,0}};

    /**
     * DFS递归函数:遍历并标记当前位置连通的所有1为已访问
     * @param map 二维网格(1表示有效区域,0表示无效)
     * @param visited 标记数组,记录位置是否被访问过
     * @param x 当前位置的行坐标
     * @param y 当前位置的列坐标
     */
    public static void dfs (int[][] map, boolean[][] visited, int x, int y) {
        // 遍历四个方向
        for(int i = 0; i < 4; i++) {
            // 计算下一个位置的坐标
            int nextX = x + dir[i][0];
            int nextY = y + dir[i][1];
            
            // 边界判断:下一个位置超出网格范围则跳过
            if (nextX < 0 || nextY < 0 || nextX >= map.length || nextY >= map[0].length) {
                continue;
            }
            
            // 核心判断:下一个位置未被访问 且 值为1 → 递归遍历
            if(!visited[nextX][nextY] && map[nextX][nextY] == 1) {
                visited[nextX][nextY] = true; // 标记为已访问
                dfs(map, visited, nextX, nextY); // 递归遍历该位置的连通区域
            }
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt(); // 网格的行数n、列数m
        int[][] map = new int[n][m]; // 存储二维网格
        
        // 输入网格数据
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                map[i][j] = sc.nextInt();
            }
        }
        
        boolean[][] visited = new boolean[n][m]; // 标记是否访问过,初始全为false
        int res = 0; // 统计连通块的数量
        
        // 遍历网格的每个位置
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                // 发现新的连通块起点:未访问 且 值为1
                if(!visited[i][j] && map[i][j] == 1) {
                    res++; // 连通块数量+1
                    visited[i][j] = true; // 标记起点为已访问
                    dfs(map, visited, i, j); // 遍历该连通块的所有位置
                }
            }
        }
        System.out.println(res); // 输出连通块总数
    }
}

3 岛屿数量广度优先搜索

3.1 思路

步骤 1:数据准备与输入读取

定义pair内部类:专门用来封装二维矩阵的坐标(x 行,y 列),方便队列存储。

定义方向数组dir:用 4 个二维数组表示 "右、左、下、上" 四个方向,避免重复写坐标计算逻辑。

读取输入:通过Scanner读取矩阵的行数n、列数m,再读取n*m个数字填充到map矩阵中。

步骤 2:遍历矩阵找连通区域起点

初始化visited矩阵:用于标记坐标是否被访问过,防止同一个 1 被重复统计。

双重循环遍历矩阵的每一个坐标(i,j):
如果该坐标未被访问且值为 1:说明找到了一个新的连通区域起点。

结果变量res加 1,然后调用bfs方法遍历这个连通区域的所有 1。

步骤 3:BFS 遍历连通区域(核心)

BFS 的核心是 "先访问当前节点,再访问所有相邻节点",确保同一个连通区域的 1 都被标记:

初始化队列:将起点坐标(x,y)入队,并标记为已访问。

循环处理队列:

取出队首坐标cur,作为当前遍历的核心节点。

遍历 4 个方向,计算相邻坐标nextX、nextY。

边界校验:排除超出矩阵范围的坐标。

有效性校验:只处理 "未被访问且值为 1" 的坐标,将其入队并标记为已访问。

队列空时,说明当前连通区域的所有 1 都已遍历完成。

步骤 4:输出结果

遍历完整个矩阵后,res的值就是连通的 1 区域的总数,直接打印即可。

3.2 实现代码

java 复制代码
import java.util.*;
import java.lang.*;

public class Main {
    // 定义一个内部类pair,用于存储二维矩阵中的坐标(x,y)
    static class pair {
        int x; // 行坐标
        int y; // 列坐标
        // 构造方法,初始化坐标
        pair(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    // 定义4个方向:右、左、下、上(对应上下左右四个相邻位置)
    public static int[][] dir = {{0,1},{0,-1},{1,0},{-1,0}};

    /**
     * BFS核心方法:遍历并标记一个连通的1区域
     * @param map 原始二维矩阵(存储0和1)
     * @param visited 标记矩阵(标记坐标是否已被访问过)
     * @param x 起始遍历的行坐标
     * @param y 起始遍历的列坐标
     */
    public static void bfs (int[][] map, boolean[][] visited, int x, int y) {
        // 初始化队列(LinkedList实现Deque),存储待遍历的坐标对
        Deque<pair> que = new LinkedList<>();
        // 将起始坐标入队
        que.offer(new pair(x, y));
        // 标记该坐标为已访问(避免重复统计)
        visited[x][y] = true;

        // 队列不为空时,持续遍历
        while (!que.isEmpty()) {
            // 取出队首坐标(当前遍历的核心坐标)
            pair cur = que.poll();
            int cur_x = cur.x; // 当前行
            int cur_y = cur.y; // 当前列

            // 遍历4个方向(上下左右)
            for (int i = 0; i < 4; i++) {
                // 计算相邻位置的坐标
                int nextX = cur_x + dir[i][0];
                int nextY = cur_y + dir[i][1];

                // 边界判断:相邻坐标是否超出矩阵范围(行/列小于0 或 行≥总行数 或 列≥总列数)
                if(nextX < 0 || nextY < 0 || nextX >= map.length || nextY >= map[0].length) {
                    continue; // 超出范围则跳过当前方向
                }

                // 条件判断:相邻坐标未被访问 且 对应位置值为1(是陆地)
                if(!visited[nextX][nextY] && map[nextX][nextY] == 1) {
                    que.offer(new pair(nextX, nextY)); // 符合条件则入队
                    visited[nextX][nextY] = true; // 立即标记为已访问(避免重复入队)
                }
            }
        }
    }

    public static void main(String[] args) {
        // 创建Scanner对象,读取输入
        Scanner sc = new Scanner(System.in);
        // 读取矩阵的行数n和列数m
        int n = sc.nextInt(), m = sc.nextInt();
        // 初始化n行m列的矩阵map
        int[][] map = new int[n][m];

        // 循环读取矩阵的每一个元素
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                map[i][j] = sc.nextInt();
            }
        }

        // 初始化访问标记矩阵,默认值为false(未访问)
        boolean[][] visited = new boolean[n][m];
        int res = 0; // 统计连通区域的数量(结果变量)

        // 遍历矩阵的每一个坐标
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                // 找到未被访问且值为1的坐标(新的连通区域起点)
                if(!visited[i][j] && map[i][j] == 1) {
                    res++; // 连通区域数量+1
                    bfs(map, visited, i, j); // 遍历该连通区域的所有1,并标记为已访问
                }
            }
        }

        // 输出最终统计的连通区域数量
        System.out.println(res);
        // 关闭Scanner(规范写法)
        sc.close();
    }
}

4 岛屿的最大面积

4.1 思路

步骤 1:初始化准备

方向数组dir:用 4 个二维数组封装 "上下左右" 四个方向,避免重复写坐标计算逻辑,简化相邻位置的遍历。

计数器count:初始值为 1,因为每个岛屿的起点(第一个 1)本身要计入面积,后续只统计相邻的 1。

输入读取:通过Scanner读取矩阵的行列数和具体数值,填充到map矩阵中;visited矩阵用于标记已统计的坐标,防止重复计算。

步骤 2:遍历矩阵找岛屿起点

双重循环遍历矩阵的每一个坐标(i,j):

只有当坐标未被访问且值为 1时,才是新岛屿的起点。
先标记起点为已访问(visited[i][j] = true),并调用DFS遍历该岛屿的所有相邻陆地。

步骤 3:DFS 递归统计岛屿面积(核心)

DFS 的核心是 "先处理当前节点的相邻节点,递归深入到底",直到没有更多相邻陆地为止:

遍历 4 个方向,计算相邻坐标nextX、nextY。

边界校验:排除超出矩阵范围的坐标(比如行号为 - 1 或超过总行数)。

有效性校验:只处理 "未被访问 + 值为 1" 的相邻坐标:

面积计数器count++(统计该陆地)。
标记该坐标为已访问(避免重复统计)。
递归调用DFS,继续遍历该相邻坐标的所有方向(直到没有更多相邻陆地)。

当递归返回时,count的值就是当前岛屿的总面积。

步骤 4:更新最大面积并输出结果

每次统计完一个岛屿的面积后,用Math.max(res, count)更新最大面积res(保留较大值)。

重置count=1,为下一个岛屿的统计做准备。

遍历完所有坐标后,res就是矩阵中最大的岛屿面积,直接输出即可。

注意:访问某个节点的时候,一定是先标记为true,再递归调用dfs。否则会无限循环,栈溢出

4.2 实现代码

java 复制代码
import java.util.*;
import java.lang.*;

public class Main {
    // 定义4个方向数组:右、左、下、上(对应上下左右四个相邻位置)
    static int[][] dir = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
    // 统计当前岛屿的面积,初始值为1(因为每个岛屿起点本身是1,先计数)
    static int count = 1;

    /**
     * DFS核心方法:递归遍历当前岛屿的所有相邻陆地,统计面积
     * @param map 原始二维矩阵(0=水域,1=陆地)
     * @param visited 访问标记矩阵(标记坐标是否已被统计,避免重复计算)
     * @param x 当前遍历的行坐标
     * @param y 当前遍历的列坐标
     */
    public static void dfs(int[][] map, boolean[][] visited, int x, int y) {
        // 遍历4个方向(上下左右),寻找相邻的陆地
        for (int i = 0; i < 4; i++) {
            // 计算相邻位置的坐标
            int nextX = x + dir[i][0];
            int nextY = y + dir[i][1];

            // 边界校验:排除超出矩阵范围的坐标(行/列小于0 或 行≥总行数 或 列≥总列数)
            if (nextX < 0 || nextY < 0 || nextX >= map.length || nextY >= map[0].length) {
                continue; // 超出范围则跳过当前方向
            }

            // 有效性校验:相邻坐标未被访问 且 是陆地(值为1)
            if (!visited[nextX][nextY] && map[nextX][nextY] == 1) {
                count++; // 岛屿面积+1(统计该相邻陆地)
                visited[nextX][nextY] = true; // 标记为已访问,避免重复统计
                dfs(map, visited, nextX, nextY); // 递归遍历该相邻陆地的所有方向
            }
        }
    }

    public static void main(String[] args) {
        // 创建Scanner对象,读取控制台输入
        Scanner sc = new Scanner(System.in);
        // 读取矩阵的行数n和列数m
        int n = sc.nextInt(), m = sc.nextInt();
        // 初始化n行m列的矩阵map,存储陆地/水域数据
        int[][] map = new int[n][m];
        // 初始化访问标记矩阵,默认值为false(未访问)
        boolean[][] visited = new boolean[n][m];

        // 循环读取矩阵的每一个元素,填充到map中
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                map[i][j] = sc.nextInt();
            }
        }

        int res = 0; // 存储最大岛屿面积(最终结果)
        // 双重循环遍历矩阵的每一个坐标
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                // 找到未被访问且值为1的坐标(新岛屿的起点)
                if (!visited[i][j] && map[i][j] == 1) {
                    visited[i][j] = true; // 标记起点为已访问
                    dfs(map, visited, i, j); // 递归遍历该岛屿的所有陆地,统计面积
                    res = Math.max(res, count); // 更新最大岛屿面积
                    count = 1; // 重置面积计数器,为下一个岛屿统计做准备
                }
            }
        }

        // 输出最大岛屿面积
        System.out.println(res);
        // 关闭Scanner(规范写法,避免资源泄漏)
        sc.close();
    }
}
相关推荐
Filotimo_2 小时前
3.4 图
算法·图论
小小unicorn2 小时前
[微服务即时通讯系统]语音子服务的实现与测试
c++·算法·微服务·云原生·架构·xcode
xsyaaaan2 小时前
代码随想录Day53图:Floyd算法精讲_ Astar算法精讲_最短路算法总结篇_图论总结
算法·图论
xu_ws2 小时前
idea新建Spring-ai项目-ollama
java·intellij-idea·ai编程
lihihi2 小时前
P10471 最大异或对 The XOR Largest Pair
算法
JTCC2 小时前
Java 设计模式西游篇 - 第九回:外观模式简化繁 如来神掌一指定
java·设计模式·外观模式
慧都小项2 小时前
JAVA开发工具IntelliJ IDEA v2026更新前瞻:更优的交互视觉,编程体验升级
java·开发语言·intellij-idea
ノBye~2 小时前
IntelliJ IDEA 2024创建项目Maven和Maven Archetype
java·maven·intellij-idea
hopsky2 小时前
idea 运行maven项目出现莫名的错误
java·maven·intellij-idea