695.岛屿的最大面积

题目描述

给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合。你可以假设二维矩阵的四个边缘都被水包围着。

找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为0。)

示例 1:

[[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:

[[0,0,0,0,0,0,0,0]]

对于上面这个给定的矩阵, 返回 0。

注意: 给定的矩阵grid 的长度和宽度都不超过 50。

一篇清晰明了的题解

网格类 DFS 的写法

这道题属于一类经典的方格搜索题目。有m×n 个小方格,组成一个网格,每个小方格与其上下左右四个方格认为是相邻的,要在这样的网格上进行某种搜索。这种题目乍一看可能有点麻烦,实际上非常简单,尤其是用 DFS 的方法。题目没有限制的话,我们尽量用 DFS 来写代码。

下面我们一步步地构造出方格类 DFS 的代码。

首先,每个方格与其上下左右的四个方格相邻,则 DFS 每次要分出四个岔:

// 基本的 DFS 框架:每次搜索四个相邻方格
void dfs(int[][] grid, int r, int c) {
    dfs(grid, r - 1, c); // 上边相邻
    dfs(grid, r + 1, c); // 下边相邻
    dfs(grid, r, c - 1); // 左边相邻
    dfs(grid, r, c + 1); // 右边相邻
}

但是,对于网格边缘的方格,上下左右并不都有邻居。一种做法是在递归调用之前判断方格的位置,例如位于左边缘,则不访问其左邻居。但这样一个一个判断写起来比较麻烦,我们可以用“先污染后治理”的方法,先做递归调用,再在每个 DFS 函数的开头判断坐标是否合法,不合法的直接返回。同样地,我们还需要判断该方格是否有岛屿(值是否为 1),否则也需要返回。

// 处理方格位于网格边缘的情况
void dfs(int[][] grid, int r, int c) {
    // 若坐标不合法,直接返回
    if (!(0 <= r && r < grid.length && 0 <= c && c < grid[0].length)) {
        return;
    }
    // 若该方格不是岛屿,直接返回
    if (grid[r][c] != 1) {
        return;
    }
    dfs(grid, r - 1, c);
    dfs(grid, r + 1, c);
    dfs(grid, r, c - 1);
    dfs(grid, r, c + 1);
}

但是这样还有一个问题:DFS 可能会不停地“兜圈子”,永远停不下来,如下图所示:
遍历会兜圈子
那么我们需要标记遍历过的方格,保证方格不进行重复遍历。标记遍历过的方格并不需要使用额外的空间,只需要改变方格中存储的值就可以。在这道题中,值为 0 表示非岛屿(不可遍历),值为 1 表示岛屿(可遍历),我们用 2 表示已遍历过的岛屿。遍历代码修改如下:

// 标记已遍历过的岛屿,不做重复遍历
void dfs(int[][] grid, int r, int c) {
    if (!(0 <= r && r < grid.length && 0 <= c && c < grid[0].length)) {
        return;
    }
    // 已遍历过(值为2)的岛屿在这里会直接返回,不会重复遍历
    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);
}

标记已遍历方格
到这里,我们代码中的重难点已经完全解决了,诸位直接看完整题解代码吧(Java):

public int maxAreaOfIsland(int[][] grid) {
    if (grid.length == 0 || grid[0].length == 0) {
        return 0;
    }

    int res = 0;
    for (int r = 0; r < grid.length; r++) {
        for (int c = 0; c < grid[0].length; c++) {
            if (grid[r][c] == 1) {
                int a = area(grid, r, c);
                res = Math.max(res, a);
            }
        }
    }
    return res;
}

int area(int[][] grid, int r, int c) {
    if (!(0 <= r && r < grid.length 
          && 0 <= c && c < grid[0].length)) {
        return 0;
    }
    if (grid[r][c] != 1) {
        return 0;
    }
    grid[r][c] = 2;
    return 1 
        + area(grid, r - 1, c)
        + area(grid, r + 1, c)
        + area(grid, r, c - 1)
        + area(grid, r, c + 1);
}

如果你觉得本文对你有帮助,欢迎关注我的公众号《面向大象编程》,其中的《LeetCode 例题精讲》系列文章正在写作,不仅有题解,更能让你学会解题的通用思路,举一反三!
wechat

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值