LeetCode岛屿问题通用解决模板

文章详细介绍了使用深度优先遍历解决岛屿问题的通用模板,包括遍历方向、边界确定、重复遍历问题处理等,并通过六道题目展示了模板的应用,如求岛屿周长、数量、最大面积、子岛屿统计和封闭岛屿数目等,强调了模板的灵活性和适应性。

前言

岛屿类问题,最简单的处理方式就是使用深度优先遍历来解,找到一个陆地后,不断的向其上下左右四个方向进行遍历,直到抵达边界或者水域为止。

我们先从一道LeetCode上的题目了解一下一般岛屿类题目的问题场景。

第一题:求岛屿的周长

在这里插入图片描述
题目了解完,我们就先来梳理一下解题思路。

模板整理

首先按照深度优先遍历的思想,肯定是先找到遍历的方式,一般网格类,我们可以按照上下左右四个方向进行遍历,再外加两个参数,r表示行,c表示列。

我们可以写出如下方法。

遍历方向

public 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);
}

假设现在位于坐标(1,1)上,那么其上下左右分别就是(0,1)、(2,1)、(1,0)、(1,2)

在这里插入图片描述
接下来,我们就要考虑边界问题,首先就是网格的边界值,其实也就是行和列的边界值。

确定边界

public void dfs(int[][] grid, int r, int c) {
   
   
    // 网格的边界值
    if (r < 0 || r >= grid.length || c < 0 || c >= grid[0].length) {
   
   
        return;
    }
    // 上
    dfs(grid, r - 1, c);
    // 下
    dfs(grid, r + 1, c);
    // 左
    dfs(grid, r, c - 1);
    // 右
    dfs(grid, r, c + 1);
}

当处理完边界值之后,我们就要考虑遍历到水域的问题了,很明显,当遍历到水域时,也就可以停止了。

public void dfs(int[][] grid, int r, int c) {
   
   
    // 网格的边界值
    if (r < 0 || r >= grid.length || c < 0 || c >= grid[0].length) {
   
   
        return;
    }
    // 当遍历到水域时,就应当停止了
    if (grid[r][c] == 0) {
   
   
        return;
    }
    // 上
    dfs(grid, r - 1, c);
    // 下
    dfs(grid, r + 1, c);
    // 左
    dfs(grid, r, c - 1);
    // 右
    dfs(grid, r, c + 1);
}

重复遍历问题处理

到此,我们已经确定了深度优先遍历的边界条件,现在我们要考虑一下实际场景的问题,按照我们现在的边界停止条件,忽略了如下的场景:

在这里插入图片描述
就是说,坐标(1,1)和(2,1)会互相作用,导致永远搜索不完,所以针对这种情况,我们还需要进行额外的处理,处理方式也很简单,一般情况下我们只需要记录一下每次走过的位置即可,当然方式有很多,比如放到Set集合,岛屿问题可以简单点处理,直接改变原有的值即可,比如直接将1改为2。

那么现在坐标(1,1)的值为2,然后要走到(2,1)

在这里插入图片描述
走到(2,1)后,我们也把值改为2,此时又从坐标(2,1)开始遍历,当向上遍历到(1,1)时就会直接停止了,因为此时(1,1)的值已经被改为2,即表示遍历过了。

在这里插入图片描述
到此为止,我们就算完成了一个标准的深度遍历模板了,现在可以用它来解题了!

模板解第一题

我们回到求岛屿的周长的问题,可以发现,所要找的岛屿的边,可以分为两种情况,一种是当遍历到网格边界时,其边界就是一条边,第二种情况就是,当遍历到水域时,也会有一条边。

在这里插入图片描述

代码如下

public int islandPerimeter(int[][] grid) {
   
   
        for (int i = 0; i < grid.length; i++) {
   
   
            for (int j = 0; j < grid[0].length; j++) {
   
   
                // 当遇到水域时,开始进行遍历
                if (grid[i][j] == 1) {
   
   
                    return dfs(grid, i, j);
                }
            }
        }
        return 0;
    }

    public int dfs(int[][] grid, int r, int c) {
   
   
        // 走向边界,周长加1
        if (r < 0 || r >= grid.length || c < 0 || c >= grid[0].length) {
   
   
            return 1;
        }
        // 走向水域,周长加1
        if (grid[r][c] == 0) {
   
   
            return 1;
        }
        // 遍历过了,周长不变,直接返回
        if (grid[r][c] == 2) {
   
   
            return 0;
        }
        // 遍历过的陆地,设置为2,以免重复遍历
        grid[r][c] = 2;

        // 最后累加所有遍历结果
        return dfs(grid, r - 1, c)
                + dfs(grid, r + 1, c)
                + dfs(grid, r, c - 1)
                + dfs(grid
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

码拉松

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

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

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

打赏作者

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

抵扣说明:

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

余额充值