(网格/岛屿问题:DFS)200. 岛屿数量、463. 岛屿的周长、695. 岛屿的最大面积、827. 最大人工岛

题目

200. 岛屿数量

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

示例 1:

输入:grid = [
[‘1’,‘1’,‘1’,‘1’,‘0’],
[‘1’,‘1’,‘0’,‘1’,‘0’],
[‘1’,‘1’,‘0’,‘0’,‘0’],
[‘0’,‘0’,‘0’,‘0’,‘0’]
]
输出:1

示例 2:

输入:grid = [
[‘1’,‘1’,‘0’,‘0’,‘0’],
[‘1’,‘1’,‘0’,‘0’,‘0’],
[‘0’,‘0’,‘1’,‘0’,‘0’],
[‘0’,‘0’,‘0’,‘1’,‘1’]
]
输出:3

提示:

m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j] 的值为 ‘0’ 或 ‘1’

463. 岛屿的周长

给定一个 row x col 的二维网格地图 grid ,其中:grid[i][j] = 1 表示陆地, grid[i][j] = 0 表示水域。

网格中的格子 水平和垂直 方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。

岛屿中没有“湖”(“湖” 指水域在岛屿内部且不和岛屿周围的水相连)。格子是边长为 1 的正方形。网格为长方形,且宽度和高度均不超过 100 。计算这个岛屿的周长。

示例 1:

在这里插入图片描述
输入:grid = [[0,1,0,0],[1,1,1,0],[0,1,0,0],[1,1,0,0]]
输出:16
解释:它的周长是上面图片中的 16 个黄色的边

示例 2:

输入:grid = [[1]]
输出:4

示例 3:

输入:grid = [[1,0]]
输出:4

提示:

row == grid.length
col == grid[i].length
1 <= row, col <= 100
grid[i][j] 为 0 或 1

695. 岛屿的最大面积

给你一个大小为 m x n 的二进制矩阵 grid 。

岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

岛屿的面积是岛上值为 1 的单元格的数目。

计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 。

示例 1:

在这里插入图片描述
输入:grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,1,1,0,1,0,0,0,0,0,0,0,0],[0,1,0,0,1,1,0,0,1,0,1,0,0],[0,1,0,0,1,1,0,0,1,1,1,0,0],[0,0,0,0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,0,0,0,0,0,0,1,1,0,0,0,0]]
输出:6
解释:答案不应该是 11 ,因为岛屿只能包含水平或垂直这四个方向上的 1 。

示例 2:

输入:grid = [[0,0,0,0,0,0,0,0]]
输出:0

提示:

m == grid.length
n == grid[i].length
1 <= m, n <= 50
grid[i][j] 为 0 或 1

827. 最大人工岛

思路

常规的深度优先算法如下

void dfs(TreeNode root) {
	// 判断 base case
    if (root == null) {
        return;
    }
    dfs(root.left);
    dfs(root.right);
}

网格DFS,改动点如下

  • 移动:变为上下左右四个节点移动
  • 避免重复遍历:遍历后的值要重新赋值,说明已被遍历
void dfs(int[][] grid, int r, int c) {
    // 判断 base case,如果坐标 (r, c) 超出了网格范围,直接返回
    if (!inArea(grid, r, c)) {
        return;
    }
    // 如果这个格子不是岛屿,直接返回
    if (grid[r][c] != 1) {
        return;
    }
    grid[r][c] = 2; // 将格子标记为「已遍历过」
    
    // 访问上、下、左、右四个相邻结点
    dfs(grid, r - 1, c);
    dfs(grid, r + 1, c);
    dfs(grid, r, c - 1);
    dfs(grid, r, c + 1);
}

// 判断坐标 (r, c) 是否在网格中
boolean inArea(int[][] grid, int r, int c) {
    return 0 <= r && r < grid.length 
        	&& 0 <= c && c < grid[0].length;
}

岛屿问题(网格DFS),常见的有下面几种类型的题目

  • L200. 岛屿数量 :当遇到 grid[i][j] == '1' 时,从此点开始做深度优先搜索 dfs,岛屿数 count + 1 且在深度优先搜索中删除此岛屿(说明此岛已被遍历了)

    如果两个点是相邻的岛屿,那么这个点便会一直遍历,直到整个岛都遍历完

  • L463. 岛屿的周长 : 可以拆分为两种类型的边

    • 网格边界:dfs 函数因为「坐标 (r, c) 超出网格范围」返回的时候,实际上就经过了一条黄色的边;
    • 海洋边界:而当函数因为「当前格子是海洋格子」返回的时候,实际上就经过了一条蓝色的
    • 值判断:
      • grid[i][j]=0:说明是海洋,返回1
      • grid[i][j]=2:说明是已经遍历的陆地,返回0
      • 当前节点超出边界,返回1
    • 返回:上下左右计算到的所有长度
      在这里插入图片描述
  • L695. 岛屿的最大面积 :每遍历到一个格子(如果是陆地),就把面积加一

  • L827. 最大人工岛(填海造陆问题):两遍 DFS,第一遍 DFS 遍历陆地格子,计算每个岛屿的面积并标记岛屿;第二遍 DFS 遍历海洋格子,观察每个海洋格子相邻的陆地格子

    • 第一次DFS:标记岛屿并计算面积,使用 DFS 遍历网格,给每个独立岛屿分配唯一编号(从 2 开始,避免与 0、1 冲突),并记录每个岛屿的面积。获取到最大的岛屿
    • 第二次DFS,评估水域变陆地后的最大面积遍历所有水域(0),检查其上下左右四个方向的相邻岛屿,通过集合去重后计算连接后的总面积(相邻岛屿面积之和 + 1),更新最大面积

      Set的add方法

      • 若集合中不存在当前添加的元素e,则将元素e加入集合,并返回true;
      • 若集合中已存在元素e(基于equals()和hashCode()判断),则不添加元素,并返回false。

      只有下面这种情况生效,所以才需要去判断set中是否有重复元素
      在这里插入图片描述
      下面的这种情况是不生效的
      在这里插入图片描述

算法

200. 岛屿数量

class Solution {
    public int numIslands(char[][] grid) {
        int count = 0;
        for (int r = 0; r < grid.length; r++) {
            for (int c = 0; c < grid[0].length; c++) {
                if (grid[r][c] == '1') {
                    dfs(grid, r, c);
                    count++;
                }
            }
        }
        return count;
    }

    private void dfs(char[][] grid, int r, int c) {
        if (!inArea(grid.length, grid[0].length, r, c)) {
            return;
        }
        if(grid[r][c] != '1'){
            return;
        }
        grid[r][c] = '2';
        dfs(grid, r + 1, c);
        dfs(grid, r, c + 1);
        dfs(grid, r - 1, c);
        dfs(grid, r, c - 1);
    }

    private boolean inArea(int maxR, int maxC, int r, int c) {
        return r >= 0 && maxR > r && c >= 0 && maxC > c;
    }
}

463. 岛屿的周长

class Solution {
    public int islandPerimeter(int[][] grid) {
        for (int r = 0; r < grid.length; r++) {
            for (int c = 0; c < grid[0].length; c++) {
                if (grid[r][c] == 1) {
                    // 其中恰好有一个岛屿
                    return dfs(grid, r, c);
                }
            }
        }
        return 0;
    }

    private int dfs(int[][] grid, int r, int c) {
        if (!inArea(grid.length, grid[0].length, r, c)) {
            return 1;
        }
        if (grid[r][c] == 0) {
            return 1;
        }
        if (grid[r][c] == 2) {
            return 0;
        }
        grid[r][c] = 2;
        return dfs(grid, r - 1, c) + dfs(grid, r, c + 1)
                + dfs(grid, r + 1, c) + dfs(grid, r, c - 1);

    }

    private boolean inArea(int maxR, int maxC, int r, int c) {
        return r >= 0 && maxR > r && c >= 0 && maxC > c;
    }
}

695. 岛屿的最大面积

class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        int max = 0;
        for (int r = 0; r < grid.length; r++) {
            for (int c = 0; c < grid[0].length; c++) {
                int area = dfs(grid, r, c);
                max = Math.max(max, area);
            }
        }
        return max;
    }

    private int dfs(int[][] grid, int r, int c) {
        if (!inArea(grid.length, grid[0].length, r, c)) {
            return 0;
        }
        if (grid[r][c] != 1) {
            return 0;
        }
        grid[r][c] = 2;
        return 1 + dfs(grid, r, c - 1) + dfs(grid, r, c + 1)
                + dfs(grid, r - 1, c) + dfs(grid, r + 1, c);
    }

    private boolean inArea(int maxR, int maxC, int r, int c) {
        return r >= 0 && maxR > r
                && c >= 0 && maxC > c;
    }
}

827. 最大人工岛

class Solution {
    public int largestIsland(int[][] grid) {
        List<Integer> area = new ArrayList<>();
        int n = grid.length;
        int ans = 0;
        for (int r = 0; r < n; r++) {
            for (int c = 0; c < n; c++) {
                int a = dfs(grid, r, c, area.size() + 2);
                area.add(a);
                ans = Math.max(a, ans);
            }
        }
        if (area.isEmpty()) {
            return 1;
        }
        // 上 下 左 右
        int[][] dirs = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
        Set<Integer> set = new HashSet<>();
        
        for (int r = 0; r < n; r++) {
            for (int c = 0; c < n; c++) {
                if (grid[r][c] != 0) {
                    continue;
                }
                // 每次遍历水域的上下左右节点都清空一遍
                set.clear();
                int newArea = 1;
                for (int[] dir : dirs) {
                    int x = c + dir[1];
                    int y = r + dir[0];
                    if (inArea(n, y, x)
                            && grid[y][x] != 0 && set.add(grid[y][x])) {
                        newArea += area.get(grid[y][x] - 2);
                    }
                }
                ans = Math.max(ans, newArea);
            }
        }
        return ans;

    }

    private int dfs(int[][] grid, int r, int c, int id) {
        if (!inArea(grid.length, r, c)) {
            return 0;
        }
        if (grid[r][c] != 1) {
            return 0;
        }
        grid[r][c] = id;
        return 1 + dfs(grid, r, c - 1, id) + dfs(grid, r, c + 1, id)
                + dfs(grid, r - 1, c, id) + dfs(grid, r + 1, c, id);

    }

    private boolean inArea(int n, int r, int c) {
        return r >= 0 && n > r && c >= 0 && n > c;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

?abc!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值