99.岛屿数量
深搜
思路:
- 遍历网格,发现岛屿:我们需要遍历整个二维网格,检查每一个位置上的元素。如果在遍历过程中遇到陆地(值为1),这意味着我们发现了一个新的岛屿。
- 深度优先搜索,标记整个岛屿:为了标记整个岛屿,我们从这个陆地开始,使用深度优先搜索(DFS)将与之相连的所有陆地都标记为已访问过。这样可以确保同一个岛屿的所有部分都被访问到,不会重复计数。
- 统计岛屿数量:每当完成一次DFS遍历,就表示已经标记完一个完整的岛屿,所以岛屿数量加1。
- 继续遍历:继续遍历网格中的下一个位置,直到所有位置都被检查过。
题解:
public class Main {
/**
* 计算岛屿的数量
*
* @param grid 输入的二维网格
* @return 岛屿的数量
*/
public static int getNum(int[][] grid) {
int count = 0; // 初始化岛屿数量为0
int n = grid.length; // 网格的行数
int m = grid[0].length; // 网格的列数
for (int i = 0; i < n; i++) { // 遍历每一行
for (int j = 0; j < m; j++) { // 遍历每一列
if (grid[i][j] == 1) { // 如果当前位置是陆地
dfs(grid, i, j); // 进行深度优先搜索,将连接的所有陆地标记
count++; // 每标记完一个岛屿,岛屿数量加1
}
}
}
return count; // 返回岛屿数量
}
/**
* 深度优先搜索(DFS)函数,用于标记连接的所有陆地
*
* @param grid 网格
* @param x 当前行
* @param y 当前列
*/
public static void dfs(int[][] grid, int x, int y) {
if (!inArea(grid, x, y)) { // 如果坐标不在网格范围内,直接返回
return;
}
if (grid[x][y] != 1) { // 如果当前位置不是陆地(已经是水或者已经标记过),直接返回
return;
}
grid[x][y] = 2; // 将当前陆地标记为2,表示已访问
// 对当前陆地的上下左右四个方向进行递归搜索
dfs(grid, x - 1, y);
dfs(grid, x + 1, y);
dfs(grid, x, y - 1);
dfs(grid, x, y + 1);
}
/**
* 判断坐标 (x, y) 是否在网格中
*
* @param grid 网格
* @param x 行坐标
* @param y 列坐标
* @return 坐标 (x, y) 是否在网格中
*/
static boolean inArea(int[][] grid, int x, int y) {
return 0 <= x && x < grid.length && 0 <= y && y < grid[0].length;
}
/**
* 主函数,读取输入并输出岛屿数量
*
* @param args 命令行参数
*/
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in); // 创建扫描器用于读取输入
int n = scanner.nextInt(); // 读取网格的行数
int m = scanner.nextInt(); // 读取网格的列数
int[][] graph = new int[n][m]; // 创建网格
// 读取网格中的每一个元素
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
graph[i][j] = scanner.nextInt();
}
}
// 输出岛屿的数量
System.out.println(getNum(graph));
}
}
广搜
思路:
使用 BFS 进行遍历,从每个未访问过的陆地开始,进行岛屿的搜索。
使用一个队列来存储待处理的陆地节点,并使用一个二维数组来标记已访问过的节点,以避免重复计数。
- 初始化:定义方向数组,包括上、下、左、右四个方向的移动。使用队列存储起始节点,并将起始节点标记为已访问。
- BFS遍历:从队列中取出节点,检查其四个相邻方向的节点:
- 如果相邻节点是未访问过的陆地(值为 1),则将其加入队列并标记为已访问。
- 继续该过程直到队列为空,表示一个完整的岛屿已经被遍历完毕。
- 计数:每当从主函数的双重循环中发现未访问的陆地(即值为 1 的格子),则调用 BFS 进行扩展,并将岛屿计数加一。
题解:
/**
* 使用BFS计算岛屿数量
*
* @param grid 输入的二维网格
* @param visited 访问标记数组
* @param x 当前节点行坐标
* @param y 当前节点列坐标
*/
static void bfs(int[][] grid, boolean[][] visited, int x, int y) {
int n = grid.length;
int m = grid[0].length;
// 方向数组:下、右、上、左
int[][] dir = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
Deque<int[]> deque = new LinkedList<>();
deque.addLast(new int[]{x, y}); // 将起始节点加入队列
visited[x][y] = true; // 标记起始节点为已访问
while (!deque.isEmpty()) {
int[] point = deque.pollFirst(); // 取出队列首个节点
int curX = point[0];
int curY = point[1];
// 遍历当前节点的四个方向
for (int i = 0; i < 4; i++) {
int newX = curX + dir[i][0];
int newY = curY + dir[i][1];
// 检查新位置是否在网格范围内,并且是未访问的陆地
if (newX >= 0 && newX < n && newY >= 0 && newY < m && grid[newX][newY] == 1 && !visited[newX][newY]) {
deque.addLast(new int[]{newX, newY}); // 将新位置加入队列
visited[newX][newY] = true; // 标记新位置为已访问
}
}
}
}
/**
* 主函数,读取输入并输出岛屿数量
*
* @param args 命令行参数
*/
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in); // 创建扫描器用于读取输入
int n = scanner.nextInt(); // 读取网格的行数
int m = scanner.nextInt(); // 读取网格的列数
int[][] graph = new int[n][m]; // 创建网格
// 读取网格中的每一个元素
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
graph[i][j] = scanner.nextInt();
}
}
boolean[][] visited = new boolean[n][m];
int count = 0;
// 遍历每个位置,如果是未访问过的陆地(值为1),则调用bfs进行岛屿扩展,并计数
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (graph[i][j] == 1 && !visited[i][j]) {
bfs(graph, visited, i, j); // 对未访问过的陆地进行BFS搜索
count++; // 计数岛屿数量
}
}
}
// 输出岛屿的数量
// System.out.println(getNum(graph));
System.out.println(count);
}
100.岛屿的最大面积
题目链接:100.岛屿的最大面积
文档讲解:代码随想录
状态:磕磕绊绊做出来了
思路:
在上一题的基础上,每遍历陆地中的一个格子,面积加一,返回最大面积。
dfs题解:
public class Main {
// 定义方向数组,表示右、下、左、上四个方向
public static int[][] dir = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};
/**
* 深度优先搜索 (DFS) 方法,计算从 (x, y) 开始的岛屿面积
*
* @param grid 二维数组表示的网格
* @param visited 二维数组标记是否访问过
* @param x 当前坐标的 x 轴位置
* @param y 当前坐标的 y 轴位置
* @return 从 (x, y) 开始的岛屿面积
*/
public static int dfs(int[][] grid, boolean[][] visited, int x, int y) {
// 检查是否越界、是否已经访问过、是否是水域
if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length || visited[x][y] || grid[x][y] == 0) {
return 0;
}
// 将当前格子标记为已访问,并将其面积记为 1
int sum = 1;
visited[x][y] = true;
// 遍历四个方向,并递归搜索相邻格子
for (int i = 0; i < 4; i++) {
int newX = x + dir[i][0];
int newY = y + dir[i][1];
sum += dfs(grid, visited, newX, newY);
}
return sum;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取网格的行数和列数
int n = scanner.nextInt();
int m = scanner.nextInt();
// 初始化网格并读取值
int[][] grid = new int[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
grid[i][j] = scanner.nextInt();
}
}
// 初始化访问标记数组
boolean[][] visited = new boolean[n][m];
int maxArea = 0;
// 遍历网格中的每一个格子
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 如果当前格子是岛屿且未被访问过,则计算从该格子开始的岛屿面积
if (!visited[i][j] && grid[i][j] == 1) {
maxArea = Math.max(maxArea, dfs(grid, visited, i, j));
}
}
}
// 输出最大的岛屿面积
System.out.println(maxArea);
}
}
bfs题解:
public static int bfs(int[][] grid) {
// 使用双端队列来存储待访问的节点
Deque<int[]> deque = new LinkedList<>();
// 获取网格的行数和列数
int n = grid.length;
int m = grid[0].length;
// 用于记录最大的岛屿面积
int maxArea = 0;
// 遍历网格中的每一个格子
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
// 如果格子中的值为1,表示这是一个岛屿的一部分
if (grid[i][j] == 1) {
// 初始化当前岛屿的面积
int sum = 1;
// 将当前格子标记为已访问
grid[i][j] = 2;
// 将当前格子的坐标加入队列
deque.addLast(new int[]{i, j});
// 进行广度优先搜索
while (!deque.isEmpty()) {
// 从队列中取出一个格子的坐标
int[] point = deque.pollFirst();
int x = point[0];
int y = point[1];
// 遍历该格子周围的四个方向
for (int k = 0; k < 4; k++) {
int newX = x + dir[k][0];
int newY = y + dir[k][1];
// 检查新的坐标是否在网格范围内,并且该格子是否未被访问过
if (newX >= 0 && newX < n && newY >= 0 && newY < m && grid[newX][newY] == 1) {
// 将新的格子标记为已访问
grid[newX][newY] = 2;
// 将新的格子的坐标加入队列
deque.addLast(new int[]{newX, newY});
// 增加当前岛屿的面积
sum++;
}
}
}
// 更新最大的岛屿面积
maxArea = Math.max(maxArea, sum);
}
}
}
// 返回最大的岛屿面积
return maxArea;
}