代码随想录第五十一天| 99.岛屿数量 深搜 99. 岛屿数量 广搜 100.岛屿的最大面积

岛屿数量问题

题目描述

给定一个由 1(陆地)和 0(水)组成的矩阵,计算岛屿的数量。岛屿由水平方向或垂直方向上相邻的陆地连接而成,并且四周都是水域。矩阵外均被水包围。

解题思路

深度优先搜索(DFS)

  1. 遍历整个矩阵,当遇到未访问过的陆地(值为1且未被标记为已访问)时,岛屿数量加1。
  2. 使用DFS标记与该陆地相连的所有陆地为已访问,避免重复计数。
  3. 通过方向数组定义上下左右四个方向,递归访问相邻陆地。

广度优先搜索(BFS)

  1. 同样遍历矩阵,遇到未访问的陆地时,岛屿数量加1。
  2. 使用队列存储待访问的陆地坐标,逐个出队并标记为已访问。
  3. 对于每个出队的陆地,检查其四个方向的相邻陆地,将符合条件的未访问陆地入队。

代码实现

DFS 实现

import java.util.Scanner;

public class Main {
    public static int[][] dir = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; // 定义四个方向

    public static void dfs(boolean[][] visited, int x, int y, int[][] grid) {
        for (int i = 0; i < 4; i++) {
            int nextX = x + dir[i][0];
            int nextY = y + dir[i][1];
            if (nextX < 0 || nextX >= grid.length || nextY < 0 || nextY >= grid[0].length) {
                continue; // 跳过越界情况
            }
            if (!visited[nextX][nextY] && grid[nextX][nextY] == 1) {
                visited[nextX][nextY] = true;
                dfs(visited, nextX, nextY, grid); // 递归访问相邻陆地
            }
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int m = sc.nextInt();
        int n = sc.nextInt();
        int[][] grid = new int[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                grid[i][j] = sc.nextInt();
            }
        }
        boolean[][] visited = new boolean[m][n];
        int ans = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (!visited[i][j] && grid[i][j] == 1) {
                    ans++;
                    visited[i][j] = true;
                    dfs(visited, i, j, grid); // 深度优先搜索标记相连陆地
                }
            }
        }
        System.out.println(ans);
    }
}

BFS 实现

import java.util.*;

public class Main {
    public static int[][] dir = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};//下右上左逆时针遍历

    public static void bfs(int[][] grid, boolean[][] visited, int x, int y) {
        Queue<pair> queue = new LinkedList<pair>();//定义坐标队列,没有现成的pair类,在下面自定义了
        queue.add(new pair(x, y));
        visited[x][y] = true;//遇到入队直接标记为优先,
        // 否则出队时才标记的话会导致重复访问,比如下方节点会在右下顺序的时候被第二次访问入队
        while (!queue.isEmpty()) {
            int curX = queue.peek().first;
            int curY = queue.poll().second;//当前横纵坐标
            for (int i = 0; i < 4; i++) {
                //顺时针遍历新节点next,下面记录坐标
                int nextX = curX + dir[i][0];
                int nextY = curY + dir[i][1];
                if (nextX < 0 || nextX >= grid.length || nextY < 0 || nextY >= grid[0].length) {
                    continue;
                }//去除越界部分
                if (!visited[nextX][nextY] && grid[nextX][nextY] == 1) {
                    queue.add(new pair(nextX, nextY));
                    visited[nextX][nextY] = true;//逻辑同上
                }
            }
        }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int m = sc.nextInt();
        int n = sc.nextInt();
        int[][] grid = new int[m][n];
        boolean[][] visited = new boolean[m][n];
        int ans = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                grid[i][j] = sc.nextInt();
            }
        }
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (!visited[i][j] && grid[i][j] == 1) {
                    ans++;
                    bfs(grid, visited, i, j);
                }
            }
        }
        System.out.println(ans);
    }
}

DFS与BFS在岛屿数量问题中的区别

搜索方式

  • DFS:在岛屿数量问题中,DFS会从一个未被访问的陆地出发,沿着一个方向(如向下、向右等)一直搜索,直到找不到新的陆地为止,然后回溯到上一个有未访问陆地的方向继续搜索,直到标记完所有与初始陆地相连的陆地。
  • BFS:从一个未被访问的陆地出发,先访问其上下左右四个方向的陆地,然后将这些陆地的上下左右方向的陆地加入队列,逐层扩展,直到所有相连的陆地都被标记为已访问。

实现方式

  • DFS:通常使用递归实现,代码较为简洁。在岛屿数量问题中,递归函数会处理当前陆地的四个方向,并对符合条件的陆地进行递归调用。
  • BFS:使用队列实现,需要显式地管理一个队列来存储待访问的陆地坐标。在岛屿数量问题中,每次从队列中取出一个陆地坐标,然后检查其四个方向的陆地,将符合条件的陆地加入队列并标记为已访问。

内存使用

  • DFS:在岛屿数量问题中,如果岛屿很大,递归深度可能会很大,可能导致栈溢出。例如,一个非常长的条形岛屿可能会使递归调用栈很深。
  • BFS:内存使用相对稳定,因为队列的大小取决于同一层的陆地数量。在岛屿数量问题中,最坏情况下队列中可能存储整个岛屿的所有陆地,但对于大多数情况,尤其是岛屿分布较分散时,内存使用较为可控。

时间复杂度

在岛屿数量问题中,无论是DFS还是BFS,时间复杂度都是O(m×n),其中m和n是矩阵的行数和列数。这是因为每个陆地都需要被访问一次,且每个陆地的四个方向都需要被检查一次。

适用场景

  • DFS:在岛屿数量问题中,如果矩阵不是特别大,或者岛屿的形状不会导致递归深度过大,DFS是一个简单且有效的选择。它适合快速实现和理解。
  • BFS:如果矩阵较大,或者岛屿可能非常长且狭窄,BFS可能更安全,因为它不会受到递归栈深度的限制。此外,如果需要在搜索过程中处理某些层次信息(例如计算岛屿的层数),BFS也更方便。

代码实现差异

DFS代码特点

// DFS代码示例(岛屿数量问题)
public static void dfs(boolean[][] visited, int x, int y, int[][] grid) {
    for (int i = 0; i < 4; i++) {
        int nextX = x + dir[i][0];
        int nextY = y + dir[i][1];
        if (nextX < 0 || nextX >= grid.length || nextY < 0 || nextY >= grid[0].length) {
            continue;
        }
        if (!visited[nextX][nextY] && grid[nextX][nextY] == 1) {
            visited[nextX][nextY] = true;
            dfs(visited, nextX, nextY, grid);
        }
    }
}
  • 使用递归直接在函数内部处理四个方向的搜索。
  • 代码较为简洁,但递归调用可能带来栈溢出风险。

BFS代码特点

// BFS代码示例(岛屿数量问题)
public static void bfs(int[][] grid, boolean[][] visited, int x, int y) {
    Queue<pair> queue = new LinkedList<>();
    queue.add(new pair(x, y));
    visited[x][y] = true;
    while (!queue.isEmpty()) {
        int curX = queue.peek().first;
        int curY = queue.poll().second;
        for (int i = 0; i < 4; i++) {
            int nextX = curX + dir[i][0];
            int nextY = curY + dir[i][1];
            if (nextX < 0 || nextX >= grid.length || nextY < 0 || nextY >= grid[0].length) {
                continue;
            }
            if (!visited[nextX][nextY] && grid[nextX][nextY] == 1) {
                queue.add(new pair(nextX, nextY));
                visited[nextX][nextY] = true;
            }
        }
    }
}
  • 使用队列显式管理待访问节点,逐层扩展。
  • 代码稍显复杂,但避免了递归带来的栈溢出问题。

100.岛屿的最大面积问题

题目描述

给定一个由 1(陆地)和 0(水)组成的矩阵,计算岛屿的最大面积。岛屿面积的计算方式为组成岛屿的陆地的总数。岛屿由水平方向或垂直方向上相邻的陆地连接而成,并且四周都是水域。矩阵外均被水包围。

解题思路

深度优先搜索(DFS)

  1. 遍历整个矩阵,当遇到未访问过的陆地(值为1且未被标记为已访问)时,开始计算该岛屿的面积。
  2. 使用DFS标记与该陆地相连的所有陆地为已访问,并统计岛屿的面积。
  3. 在遍历过程中,维护一个最大面积变量,每次计算出岛屿面积时更新最大值。

广度优先搜索(BFS)

  1. 同样遍历矩阵,遇到未访问的陆地时,开始计算该岛屿的面积。
  2. 使用队列存储待访问的陆地坐标,逐个出队并标记为已访问,同时统计岛屿面积。
  3. 对于每个出队的陆地,检查其四个方向的相邻陆地,将符合条件的未访问陆地入队并计数。
  4. 每次完成一个岛屿的面积计算后,更新最大面积。

代码实现

DFS 实现

import java.util.Scanner;

public class Main {
    public static int[][] dir = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; // 定义四个方向

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int m = sc.nextInt();
        int n = sc.nextInt();
        int[][] grid = new int[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                grid[i][j] = sc.nextInt();
            }
        }
        boolean[][] visited = new boolean[m][n];
        int max = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (!visited[i][j] && grid[i][j] == 1) {
                    visited[i][j] = true;
                    max = Math.max(max, dfs(visited, i, j, grid));
                }
            }
        }
        System.out.println(max);
    }

    public static int dfs(boolean[][] visited, int x, int y, int[][] grid) {
        int size = 1;
        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 >= grid.length || nextY >= grid[0].length) {
                continue;
            }
            if (!visited[nextX][nextY] && grid[nextX][nextY] == 1) {
                visited[nextX][nextY] = true;
                size = size + dfs(visited, nextX, nextY, grid);
            }
        }
        return size;
    }
}

BFS 实现

import java.util.*;

public class Main {
    static class Node {
        int x;
        int y;

        public Node(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    static final int[][] dir = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    static int result = 0;
    static int count = 0;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        int[][] map = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                map[i][j] = scanner.nextInt();
            }
        }
        boolean[][] visited = new boolean[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (!visited[i][j] && map[i][j] == 1) {
                    count = 0;
                    bfs(map, visited, i, j);
                    result = Math.max(count, result);
                }
            }
        }
        System.out.println(result);
    }

    static void bfs(int[][] map, boolean[][] visited, int x, int y) {
        Queue<Node> q = new LinkedList<>();
        q.add(new Node(x, y));
        visited[x][y] = true;
        count++;
        while (!q.isEmpty()) {
            Node node = q.remove();
            for (int i = 0; i < 4; i++) {
                int nextX = node.x + dir[i][0];
                int nextY = node.y + dir[i][1];
                if (nextX < 0 || nextY < 0 || nextX >= map.length || nextY >= map[0].length || visited[nextX][nextY] || map[nextX][nextY] == 0) {
                    continue;
                }
                q.add(new Node(nextX, nextY));
                visited[nextX][nextY] = true;
                count++;
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值