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();
}
}