网格图--Day01--网格图DFS--200. 岛屿数量,695. 岛屿的最大面积,3619. 总价值可以被 K 整除的岛屿数目

网格图–Day01–网格图DFS–200. 岛屿数量,695. 岛屿的最大面积,3619. 总价值可以被 K 整除的岛屿数目

今天要训练的题目类型是:【网格图DFS】,题单来自@灵艾山茶府

适用于需要计算连通块个数、大小的题目。

部分题目做法不止一种,也可以用 BFS 或并查集解决。

DFS函数中的三步曲:判断,处理,继续DFS

  • 判断:是否越界,是否是需要DFS的格子
  • 处理:根据题意处理格子
  • 继续DFS:DFS四个方向,有时候可能需要收集返回值。

200. 岛屿数量

思路【我】:

  • 使用visited数组,标记格子是否被访问过。
  • 如果是岛屿,且没有被访问过,进去开始DFS。
    • 标记已访问。
    • 向右下左上四个方向探索。首先判断索引是否越界,接着判断是不是岛屿,再判断岛屿是否被访问过。条件都满足的话,DFS这个格子。
class Solution {

    private final int[][] DIRS = new int[][] { { 1, 0 }, { 0, 1 }, { 0, -1 }, { -1, 0 } };

    private void dfs(char[][] grid, boolean[][] visited, int x, int y) {
        visited[x][y] = true;
        for (int i = 0; i < 4; i++) {
            int nextX = x + DIRS[i][0];
            int nextY = y + DIRS[i][1];
            if (xyOK(grid, nextX, nextY) && grid[nextX][nextY] == '1' && !visited[nextX][nextY]) {
                dfs(grid, visited, nextX, nextY);
            }
        }
    }

    private boolean xyOK(char[][] grid, int x, int y) {
        if (x < 0 || y < 0 || x >= grid.length || y >= grid[0].length) {
            return false;
        }
        return true;
    }

    public int numIslands(char[][] grid) {
        int n = grid.length;
        int m = grid[0].length;
        int count = 0;
        boolean[][] visited = new boolean[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] == '1' && !visited[i][j]) {
                    dfs(grid, visited, i, j);
                    count++;
                }
            }
        }
        return count;
    }
}

思路【@灵艾山茶府】:

不同之处:

  • 省去标记数组,直接赋值grid=2来标记已访问。
  • 进入DFS之后,再判断索引,是否是陆地,是否已访问。
class Solution {

    private final int[][] DIRS = new int[][] { { 1, 0 }, { 0, 1 }, { 0, -1 }, { -1, 0 } };

    private void dfs(char[][] grid, int i, int j) {
        if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] != '1') {
            return;
        }
        grid[i][j] = '2';
        for (int k = 0; k < 4; k++) {
            int x = i + DIRS[k][0];
            int y = j + DIRS[k][1];
            dfs(grid, x, y);
        }
    }

    public int numIslands(char[][] grid) {
        int n = grid.length;
        int m = grid[0].length;
        int count = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] == '1') {
                    dfs(grid, i, j);
                    count++;
                }
            }
        }
        return count;
    }
}

另一个版本:

区别是:先判断条件,再进DFS。这样写可以减少递归次数,降低时空复杂度。但是风险会更大,如果条件写得不够好,可能会死循环。

private void dfs(char[][] grid, int i, int j) {
    grid[i][j] = '2';
    for (int k = 0; k < 4; k++) {
        int x = i + DIRS[k][0];
        int y = j + DIRS[k][1];
        if (!(x < 0 || y < 0 || x >= grid.length || y >= grid[0].length) && grid[x][y] == '1') {
            dfs(grid, x, y);
        }
    }
}

695. 岛屿的最大面积

思路【我】:

使用全局变量area和maxArea。

  • 在主函数发现新岛屿的时候,把area置0.
  • 每进一个格子就area++,
  • 回到主函数的时候,这个岛屿遍历完了,更新maxArea。
class Solution {

    private final int[][] DIRS = new int[][] { { 1, 0 }, { 0, 1 }, { 0, -1 }, { -1, 0 } };
    private int area = 0;
    private int maxArea = 0;

    private void dfs(int[][] grid, int i, int j) {
        if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] != 1) {
            return;
        }
        area++;
        grid[i][j] = 2;
        for (int k = 0; k < 4; k++) {
            int x = i + DIRS[k][0];
            int y = j + DIRS[k][1];
            dfs(grid, x, y);
        }
    }

    public int maxAreaOfIsland(int[][] grid) {
        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) {
                    area = 0;
                    dfs(grid, i, j);
                    maxArea = Math.max(maxArea, area);
                }
            }
        }
        return maxArea;
    }

}

思路【@灵艾山茶府】:

使用递归函数的返回值,累加面积返回到上层。

class Solution {

    private final int[][] DIRS = new int[][] { { 1, 0 }, { 0, 1 }, { 0, -1 }, { -1, 0 } };

    private int dfs(int[][] grid, int i, int j) {
        if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] != 1) {
            return 0;
        }
        int area = 1;
        grid[i][j] = 2;
        for (int k = 0; k < 4; k++) {
            int x = i + DIRS[k][0];
            int y = j + DIRS[k][1];
            // 注意这里是加等于。和当前格子相连的四个方向的格子,都是同一个岛屿。
            area += dfs(grid, x, y);
        }
        return area;
    }

    public int maxAreaOfIsland(int[][] grid) {
        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++) {
                if (grid[i][j] == 1) {
                    int area = dfs(grid, i, j);
                    maxArea = Math.max(maxArea, area);
                }
            }
        }
        return maxArea;
    }

}

3619. 总价值可以被 K 整除的岛屿数目

思路:

上一题每个格子的价值为1,现在每个格子有具体的价值。所以代码照抄。

需要改的地方:

  • 不能赋值为2作为标记了,要赋值为0
  • 返回值需要为long类型,观察题目要求,最大的val可以去到10的11次方。
class Solution {

    private final int[][] DIRS = new int[][] { { 1, 0 }, { 0, 1 }, { 0, -1 }, { -1, 0 } };

    private long dfs(int[][] grid, int i, int j) {
        if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == 0) {
            return 0;
        }
        // 获取当前岛屿的价值
        long val = grid[i][j];
        // 赋值为0,将它变为水
        grid[i][j] = 0;
        for (int k = 0; k < 4; k++) {
            int x = i + DIRS[k][0];
            int y = j + DIRS[k][1];
            // 注意这里是加等于。和当前格子相连的四个方向的格子,都是同一个岛屿。
            val += dfs(grid, x, y);
        }
        return val;
    }

    public int countIslands(int[][] grid, int k) {
        int n = grid.length;
        int m = grid[0].length;
        int count = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] != 0) {
                    long val = dfs(grid, i, j);
                    count += val % k == 0 ? 1 : 0;
                }
            }
        }
        return count;
    }
}

到这里,可以总结DFS函数中的三步曲:判断,处理,继续DFS

  • 判断:是否越界,是否是需要DFS的格子
  • 处理:根据题意处理格子
  • 继续DFS:DFS四个方向,有时候可能需要收集返回值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值