leetcode----dfs解决岛屿问题(200、1254、1020、695、1905)

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

思路:

  • dfs

核心思路就是利用dfs算法遍历二维数组。

将二维数组的每一个元素看作一个节点,它相邻的上下左右元素就是它的相邻节点。这样就抽象出一个网状图。然后根据二叉树的dfs写出二维数组的dfs框架

// 二叉树遍历框架
void traverse(TreeNode root) {
    traverse(root.left);
    traverse(root.right);
}

// 二维矩阵遍历框架
void dfs(int[][] grid, int i, int j, boolean[][] visited) {
    int m = grid.length, n = grid[0].length;
    if (i < 0 || j < 0 || i >= m || j >= n) {
        // 超出索引边界
        return;
    }
    if (visited[i][j]) {
        // 已遍历过 (i, j)
        return;
    }
    // 进入节点 (i, j)
    visited[i][j] = true;
    dfs(grid, i - 1, j); // 上
    dfs(grid, i + 1, j); // 下
    dfs(grid, i, j - 1); // 左
    dfs(grid, i, j + 1); // 右
}

基本思路是这样的。我们依次遍历数组中的每一个节点

  • grid[i][j] == '0',跳过,不做任何操作;
  • grid[i][j] == '1',岛屿数量+1,并就寻找所有值为1的和它相连的节点。注意,在寻找的过程中,需要使用一个备忘录visited二维数组来记录当前节点是否被遍历过,防止走回头路,若遍历过,即visited[i][j] == true,则返回,不再对这个节点进行dfs寻找关联节点操作。

实际上可以在寻找的过程中将该岛慢慢淹了,这也可以防止走回头路,不需要开辟额外的空间。所谓将岛屿淹了的意思就是,若当前节点为'1',则在dfs中将这个节点的值设置为'0',然后将它关联的、值为'1'的节点也"淹了"。

class Solution {
    private int m = 0;
    private int n = 0;

    public int numIslands(char[][] grid) {
        m = grid.length;
        n = grid[0].length;
        int res = 0;
        for(int i = 0; i < m; ++i){
            for(int j =  0; j < n; ++j){
                if(grid[i][j] == '1'){
                    //岛屿数量+1
                    res++;
                    //淹没这个岛屿
                    dfs(grid, i, j);
                }
            }
        }
        return res;
    }

    //方向数组
    private int[][] dirs = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    private void dfs(char[][] chars, int row, int col){
        //索引越界检查
        if(row < 0 || col < 0 || row >= m || col >= n){
            return;
        }
		
        //
        if(chars[row][col] == '1'){
            //淹没当前陆地
            chars[row][col] = '0';
        } else {
            //已经是海水了,直接返回
            return;
        }

        //淹没相邻陆地
        for(int[] dir: helper){
            int nextRow = row + dir[0];
            int nextCol = col + dir[1];
            dfs(chars, nextRow, nextCol);
        }
    }
}

1254.统计封闭岛屿的数目

问题:有一个二维矩阵 grid ,每个位置要么是陆地(记号为 0 )要么是水域(记号为 1 )。

我们从一块陆地出发,每次可以往上下左右 4 个方向相邻区域走,能走到的所有陆地区域,我们将其称为一座「岛屿」。

如果一座岛屿 完全 由水域包围,即陆地边缘上下左右所有相邻区域都是水域,那么我们将其称为 「封闭岛屿」。

请返回封闭岛屿的数目。
示例:
在这里插入图片描述

输入:grid = [[1,1,1,1,1,1,1,0],[1,0,0,0,0,1,1,0],[1,0,1,0,1,1,1,0],[1,0,0,0,0,1,0,1],[1,1,1,1,1,1,1,0]]
输出:2
解释:
灰色区域的岛屿是封闭岛屿,因为这座岛屿完全被水域包围(即被 1 区域包围)。

思路:这道题属于200.岛屿数量的变种。封闭岛屿其实就是在前面【岛屿数量】这道题的基础上 排除数组边界也就是靠边的岛屿 剩下的就是封闭岛屿了。我们只需要将边界岛屿淹掉,剩下的就和【岛屿数量】这道题一样了。

需要注意的是,这道题是用整数表示海水和陆地,且0表示陆地,1表示海水。

代码如下:

class Solution {
    public int closedIsland(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        //淹掉第一行和最后一行的  n表示列数
        for(int j = 0; j < n; ++j){
            dfs(grid, 0, j);
            dfs(grid, m - 1, j);
        }
		
        //淹掉第一列和最后一列    m表示列数
        for(int i = 1; i < m; ++i){
            dfs(grid, i, 0);
            dfs(grid, i, n - 1);
        }

        int res = 0;
        for(int i = 1; i < m - 1; ++i){
            for(int j = 1; j < n - 1; ++j){
                if(grid[i][j] == 0){
                    res++;
                    dfs(grid, i, j);
                }
            }
        }
        return res;
    }

    private int[][] dirs = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    private void dfs(int[][] grid, int row, int col){
        if(row < 0 || col < 0 || row >= grid.length || col >= grid[0].length){
            return;
        }

        if(grid[row][col] == 1){
            return;
        } else {
            grid[row][col] = 1;
        }

        for(int[] dir: dirs){
            int nextRow = row + dir[0];
            int nextCol = col + dir[1];
            dfs(grid, nextRow, nextCol);
        }
    }
}

1020.飞地的数量

问题:给出一个二维数组 A,每个单元格为 0(代表海)或 1(代表陆地)。

移动是指在陆地上从一个地方走到另一个地方(朝四个方向之一)或离开网格的边界。

返回网格中无法在任意次数的移动中离开网格边界的陆地单元格的数量。

示例1:

输入:[[0,0,0,0],[1,0,1,0],[0,1,1,0],[0,0,0,0]]
输出:3
解释: 
有三个 1 被 0 包围。一个 1 没有被包围,因为它在边界上。

思路:这道题的思路和1254.统计封闭岛屿的数目基本一致,都需要淹掉靠边的陆地,不同的是【统计封闭岛屿的数目】需要求岛屿的数目,而这道题需要求的是面积。

我们将靠边的岛屿淹没之后,然后再遍历这个二维数组,统计出1的个数,即为【飞地的数量】

class Solution {
    public int numEnclaves(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        //淹掉第一行和最后一行的  注意:n表示列数
        for(int j = 0; j < n; ++j){
            dfs(grid, 0, j);
            dfs(grid, m - 1, j);
        }
		
        //淹掉第一列和最后一列    注意:m表示列数
        for(int i = 1; i < m; ++i){
            dfs(grid, i, 0);
            dfs(grid, i, n - 1);
        }

        int res = 0;
        for(int i = 0; i < m; ++i){
            for(int j = 0; j < n; ++j){
                if(grid[i][j] == 1){
                    res += 1;
                }
            }
        }
        return res;
    }

    private int[][] dirs = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    private void dfs(int[][] grid, int row, int col){
        if(row < 0 || col < 0 || row >= grid.length || col >= grid[0].length){
            return;
        }

        if(grid[row][col] == 0){
            //海水
            return;
        } else{
            //陆地  淹掉
            grid[row][col] = 0;
        }

        for(int[] dir: dirs){
            int nextRow = row + dir[0];
            int nextCol = col + dir[1];
            dfs(grid, nextRow, nextCol);
        }
    }
}

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 。

思路:思路和200.岛屿数量一样,只需要再淹没岛屿的时候记录这个岛屿的面积即可。具体做法就是为dfs函数设置返回值。

class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        int m = grid.length, n = grid[0].length;

        int res = 0;
        for(int i = 0; i < m; ++i){
            for(int j = 0; j < n; ++j){
                if(grid[i][j] == 1){
                    int temp = dfs(grid, i, j);
                    res = Math.max(res, temp);
                }
            }
        }
        return res;
    }

    private int dfs(int[][] grid, int row, int col){
        if(row < 0 || col < 0 || row >= grid.length || col >= grid[0].length){
            return 0;
        }

        if(grid[row][col] == 0){
            return 0;
        } else {
            grid[row][col] = 0;
        }
        int res = 1;
        res += dfs(grid, row, col + 1) + dfs(grid, row, col - 1) + dfs(grid, row + 1, col) + dfs(grid, row - 1, col);
        return res;
    }
}

这里还有个偷懒的做法:设置一个成员变量,用来记录每个岛屿的面积。然后与上一个岛屿面积比较,依次循环,直到遍历完整个二维数组。

class Solution {
    private int temp = 0;
    public int maxAreaOfIsland(int[][] grid) {
        int m = grid.length, n = grid[0].length;

        int res = 0;
        for(int i = 0; i < m; ++i){
            for(int j = 0; j < n; ++j){
                if(grid[i][j] == 1){
                    dfs(grid, i, j);
                    res = Math.max(res, temp);
                    temp = 0;
                }
            }
        }
        return res;
    }

    private void dfs(int[][] grid, int row, int col){
        if(row < 0 || col < 0 || row >= grid.length || col >= grid[0].length){
            return;
        }

        if(grid[row][col] == 0){
            return;
        } else {
            grid[row][col] = 0;
            temp++;
        }

        dfs(grid, row, col + 1);
        dfs(grid, row, col - 1);
        dfs(grid, row + 1, col);
        dfs(grid, row - 1, col);
    }
}

1905.统计子岛屿

问题:给你两个m x n的二进制矩阵grid1grid2 ,它们只包含0(表示水域)和 1(表示陆地)。一个 岛屿 是由 四个方向 (水平或者竖直)上相邻的 1组成的区域。任何矩阵以外的区域都视为水域。

如果 grid2 的一个岛屿,被 grid1 的一个岛屿 完全 包含,也就是说 grid2 中该岛屿的每一个格子都被 grid1 中同一个岛屿完全包含,那么我们称 grid2 中的这个岛屿为 子岛屿 。

请你返回 grid2 中 子岛屿 的 数目 。

示例:

输入:grid1 = [[1,1,1,0,0],[0,1,1,1,1],[0,0,0,0,0],[1,0,0,0,0],[1,1,0,1,1]], grid2 = [[1,1,1,0,0],[0,0,1,1,1],[0,1,0,0,0],[1,0,1,1,0],[0,1,0,1,0]]
输出:3
解释:如上图所示,左边为 grid1 ,右边为 grid2 。
grid2 中标红的 1 区域是子岛屿,总共有 3 个子岛屿。

思路:首先需要明白: 子岛屿的概念:当grid2中的岛屿的所有陆地在grid1中也是陆地,那么这个岛屿就是grid1中某个岛屿的子岛屿。

反过来说,当grid2中的某一块陆地在grid1中的对应位置是海水,那么这块陆地所在的岛屿肯定不会是grid1中某块岛屿的子岛屿了。

所以,基本思路就是:

  • grid2中的某一块陆地在grid1中的对应位置是海水时,即grid1[i][j] == 0 && grid2[i][j] == 1,就将这块陆地所在的岛屿淹掉,那么剩下的岛屿就是子岛屿,然后再统计其数量,即为所求。代码如下:
class Solution {
    public int countSubIslands(int[][] grid1, int[][] grid2) {
        int m = grid2.length, n = grid2[0].length;

        for(int i = 0; i < m; ++i){
            for(int j = 0; j < n; ++j){
                if(grid1[i][j] == 0 && grid2[i][j] == 1){
                    dfs(grid2, i, j);
                }
            }
        }

        int res = 0;
        for(int i = 0; i < m; ++i){
            for(int j = 0; j < n; ++j){
                if(grid2[i][j] == 1){
                    res++;
                    dfs(grid2, i, j);
                }
            }
        }
        return res;
    }

    int[][] dirs = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    private void dfs(int[][] grid, int row, int col){
         //索引越界检查
        if(row < 0 || col < 0 || row >= grid.length || col >= grid[0].length){
            return;
        }

        if(grid[row][col] == 0){
            return;
        } else {
            grid[row][col] = 0;
        }

        for(int[] dir: dirs){
            int nextRow = row + dir[0];
            int nextCol = col + dir[1];
            dfs(grid, nextRow, nextCol);
        }

    }
}

整理思路,记录博客,以便复习。若有误,望指正~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LeetCode-Editor是一种在线编码工具,它提供了一个用户友好的界面编写和运行代码。在使用LeetCode-Editor时,有时候会出现乱码的问题。 乱码的原因可能是由于编码格式不兼容或者编码错误导致的。在这种情况下,我们可以尝试以下几种解决方法: 1. 检查文件编码格式:首先,我们可以检查所编辑的文件的编码格式。通常来说,常用的编码格式有UTF-8和ASCII等。我们可以将编码格式更改为正确的格式。在LeetCode-Editor中,可以通过界面设置或编辑器设置来更改编码格式。 2. 使用正确的字符集:如果乱码是由于使用了不同的字符集导致的,我们可以尝试更改使用正确的字符集。常见的字符集如Unicode或者UTF-8等。在LeetCode-Editor中,可以在编辑器中选择正确的字符集。 3. 使用合适的编辑器:有时候,乱码问题可能与LeetCode-Editor自身相关。我们可以尝试使用其他编码工具,如Text Editor、Sublime Text或者IDE,看是否能够解决乱码问题。 4. 查找特殊字符:如果乱码问题只出现在某些特殊字符上,我们可以尝试找到并替换这些字符。通过仔细检查代码,我们可以找到导致乱码的特定字符,并进行修正或替换。 总之,解决LeetCode-Editor乱码问题的方法有很多。根据具体情况,我们可以尝试更改文件编码格式、使用正确的字符集、更换编辑器或者查找并替换特殊字符等方法来解决这个问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值