岛屿问题(用DFS遍历二维数组)

​​​​​​一文秒杀所有岛屿题目 :: labuladong的算法小抄 (gitee.io)

200. 岛屿数量 - 力扣(LeetCode)

给你一个由 '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算法/BFS算法遍历二维数组,将二维矩阵中的每一个位置看做一个节点,这个节点的上下左右四个位置就是相邻节点,那么整个矩阵就可以抽象成一幅网状的图结构。因为是遍历一幅图,所以遍历的过程中还需要一个 visited 布尔数组防止走回头路。

这道题中没有使用visited数组,而是选择每次遇到岛屿时,都用DFS算法把岛屿淹掉,避免了维护visited数组。

class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        int res=0;
        for(int i=0;i<grid.size();i++)
        {
            for(int j=0;j<grid[0].size();j++)
            {
                if(grid[i][j]=='1')//grid[i][j]是陆地
                {
                    dfs(i,j,grid);
                    res++;
                }
            }
        }
        return res;
    }
    //从grid[i][j]开始,把与之相邻的陆地都淹掉
    void dfs(int i,int j,vector<vector<char>>& grid)
    {
        int m=grid.size();
        int n=grid[0].size();
        //处理越界
        if(i<0||i>=m||j<0||j>=n)
        {
            return ;
        }
        if(grid[i][j]=='0')//已经是海水了
        {
            return ;
        }
        grid[i][j]='0';//把grid[i][j]淹掉
        //把grid[i][j]的上下左右都淹掉
        dfs(i-1,j,grid);
        dfs(i+1,j,grid);
        dfs(i,j-1,grid);
        dfs(i,j+1,grid);
    }
};

1254. 统计封闭岛屿的数目

二维矩阵 grid 由 0 (土地)和 1 (水)组成。岛是由最大的4个方向连通的 0 组成的群,封闭岛是一个 完全 由1包围(左、上、右、下)的岛。

请返回 封闭岛屿 的数目。

示例 1:

输入: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 区域包围)。

 思路:

由题意得,靠边的陆地不算封闭岛屿,所以先用dfs把四条边上的陆地都排除掉,剩下的就是封闭岛屿。

class Solution {
public:
    int closedIsland(vector<vector<int>>& grid) {
        int m=grid.size();
        int n=grid[0].size();
        int res=0;
        //把边上的岛都淹掉
        for(int i=0;i<m;i++)
        {
            dfs(grid,i,0);//把左边的岛都淹掉
            dfs(grid,i,n-1);//把右边的岛都淹掉
        }
        for(int j=0;j<n;j++)
        {
            dfs(grid,0,j);//把上边的岛都淹掉
            dfs(grid,m-1,j);//把下边的岛都淹掉
        }
        //遍历grid,此时剩下的岛屿都是封闭岛
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(grid[i][j]==0)
                {
                    res++;
                    dfs(grid,i,j);
                }
            }
        }
        return res;
    }
    //从grid[i][j]开始,把与之相邻的陆地都淹掉
    void dfs(vector<vector<int>>& grid,int i,int j)
    {
        int m=grid.size();
        int n=grid[0].size();
        //处理越界
        if(i<0||j<0||i>=m||j>=n)
        {
            return ;
        }
        //已经是海水了
        if(grid[i][j]==1)
        {
            return ;
        }
        //把grid[i][j]淹掉
        grid[i][j]=1;
        //把grid[i][j]的上下左右都淹掉
        dfs(grid,i+1,j);
        dfs(grid,i-1,j);
        dfs(grid,i,j+1);
        dfs(grid,i,j-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

思路:
给dfs设置返回值,记录每次淹没的陆地的面积
class Solution {
public:
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        int res=0;
        int m=grid.size();
        int n=grid[0].size();
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(grid[i][j]==1)
                {
                    int cover=dfs(grid,i,j);
                    res=max(cover,res);//选取面积最大的岛屿
                }
            }
        }
        return res;
    }
    //淹没与grid[i][j]相邻的陆地,并返回淹没陆地的面积
    int dfs(vector<vector<int>>& grid,int i,int j)
    {
        int m=grid.size();
        int n=grid[0].size();
        if(i<0||j<0||i>=m||j>=n)
        {
            return 0;
        }
        if(grid[i][j]==0)
        {
            return 0;
        }   
        grid[i][j]=0;

        return dfs(grid,i+1,j)
                +dfs(grid,i-1,j)
                +dfs(grid,i,j+1)
                +dfs(grid,i,j-1)+1;
    }
};

1905. 统计子岛屿 - 力扣(LeetCode)

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

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

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

示例 1:


输入: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 个子岛屿。

思路:

如果岛屿2中存在一片陆地,在岛屿1的对应位置是海水,那么岛屿2就不是岛屿1的子岛 ,

排除2中肯定不是子岛的岛屿

现在2中剩下的肯定都是子岛,就可以计算子岛数量了

class Solution {
public:
    int countSubIslands(vector<vector<int>>& grid1, vector<vector<int>>& grid2) {
        int m=grid2.size();
        int n=grid2[0].size();
        //如果岛屿2中存在一片陆地,在岛屿1的对应位置是海水,那么岛屿2就不是岛屿1的子岛
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(grid2[i][j]==1&&grid1[i][j]==0)//淹掉2中肯定不是子岛的岛屿
                {
                    dfs(grid2,i,j);
                }
            }
        }
        int res=0;
        for(int i=0;i<m;i++)//现在2中剩下的肯定都是子岛,计算子岛数量
        {
            for(int j=0;j<n;j++)
            {
                if(grid2[i][j]==1)
                {
                    dfs(grid2,i,j);
                    res++;
                }
            }
        }
        return res;
    }
    void dfs(vector<vector<int>>& grid2,int i,int j)
    {
        int m=grid2.size();
        int n=grid2[0].size();
        if(i<0||j<0||i>=m||j>=n)
        {
            return ;
        }
        if(grid2[i][j]==0)
        {
            return ;
        }
        grid2[i][j]=0;
        dfs(grid2,i+1,j);
        dfs(grid2,i-1,j);
        dfs(grid2,i,j+1);
        dfs(grid2,i,j-1);
    }
};

827. 最大人工岛

给你一个大小为 n x n 二进制矩阵 grid 。最多 只能将一格 0 变成 1 。

返回执行此操作后,grid 中最大的岛屿面积是多少?

岛屿 由一组上、下、左、右四个方向相连的 1 形成。

示例 1:

输入: grid = [[1, 0], [0, 1]]
输出: 3
解释: 将一格0变成1,最终连通两个小岛得到面积为 3 的岛屿。

示例 2:

输入: grid = [[1, 1], [1, 0]]
输出: 4
解释: 将一格0变成1,岛屿的面积扩大为 4。

示例 3:

输入: grid = [[1, 1], [1, 1]]
输出: 4
解释: 没有0可以让我们变成1,面积依然为 4。

 思路:

  1. 首先dfs找岛屿,找到一块就淹了它,并且给这个岛屿标记一下,存入HashMap(Key:岛屿的序号,Value,岛屿的面积)。给第一块岛屿标记为flag = -1,同时,在找的过程中,把访问到的格子的值改成flag。然后每当找到一块,flag--。有两个好处,第一,每块岛屿都被唯一标记了。第二,因为格子的值改成了小于0的flag,判断格子的值是否<=0就等效于visited数组的功能,使得回溯的过程中不会再访问已经访问过的格子。

  2. 对于已经标记完岛屿的地图,只需要挨个遍历,找到每个海洋(值为0)的格子,然后试一下上下左右是否可以连通,若联通,就计算一下联通之后的最大面积,更新答案。在这个过程中,需要用set判断上下左右有没有相同的岛屿,即每个岛屿只能加一次。

  3. 返回最大的答案

例:grid={                         遍历之后----》                          grid={

                {1,0,1},                                                                        {-1,0,-2},

                {0,0,0},                                                                        {0,0,0},

                {0,1,1}                                                                         {0,-3,-3}             

                }                                                                                  }

unoedered_map<int,int> mapping={ ( -1 , 1 ),( -2 , 1 ),( -3 , 2 ) } 。

class Solution {
public:
    int largestIsland(vector<vector<int>>& grid) {
        unordered_map<int,int> mapping;
        int n=grid.size();
        int flag=-1;//序号从-1开始递减,防止dfs时走回头路
        for(int i=0;i<n;i++)//将grid中各个岛屿的面积都与序号flag联系起来
        {
            for(int j=0;j<n;j++)
            {
                if(grid[i][j]==1)
                {
                    int cover=dfs(grid,i,j,flag);
                    mapping[flag]=cover;
                    flag--;
                }
            }
        }
        int res=0;
        set<int> s;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                int cover=0;
                if(grid[i][j]==0)//假设将grid[i][j]变成陆地
                {
                    int up=0,down=0,left=0,right=0;//记录grid[i][j]的上下左右情况
                    if(i>0) up=grid[i-1][j];
                    if(i<n-1) down=grid[i+1][j];
                    if(j>0) left=grid[i][j-1];
                    if(j<n-1) right=grid[i][j+1];
                    //加入set中去重
                    s.insert(up);
                    s.insert(down);
                    s.insert(left);
                    s.insert(right);
                    for(int dir:s)
                    {
                        if(dir<0)//小于0,那它就是一块陆地
                        {
                            cover+=mapping[dir];
                        }
                    }
                    s.clear();//存完grid[i][j]的上下左右情况,记得要清空set
                    cover++;//加上假设的那块陆地
                }
                res=max(res,cover);//求出最大面积
            }
        }
        if(res==0)//如果grid全是陆地,那么最后res应该为n*n
        {
            res=n*n;
        }
        return res;
    }

    int dfs(vector<vector<int>>& grid,int i,int j,int flag)
    {
        int n=grid.size();
        if(i>=n||j>=n||i<0||j<0)//超出边界,返回0
            return 0;
        if(grid[i][j]<=0)//如果是海水或者其他已经被淹掉的陆地,那么就返回0
            return 0;
        if(grid[i][j]==1)//如果是陆地,就用flag把它淹掉(用flag标记它)
            grid[i][j]=flag;
        return dfs(grid,i+1,j,flag)+
               dfs(grid,i-1,j,flag)+
               dfs(grid,i,j+1,flag)+
               dfs(grid,i,j-1,flag)+1;
    }
};

934. 最短的桥

给你一个大小为 n x n 的二元矩阵 grid ,其中 1 表示陆地,0 表示水域。

 是由四面相连的 1 形成的一个最大组,即不会与非组内的任何其他 1 相连。grid 中 恰好存在两座岛 。

你可以将任意数量的 0 变为 1 ,以使两座岛连接起来,变成 一座岛 。

返回必须翻转的 0 的最小数目。

示例 1:

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

示例 2:

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

示例 3:

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

思路:

最小翻转数 = 岛屿1 与 岛屿2 之间的最短路径
dfs(遍历二维数组) + bfs(找到最短路径)
如何找到最短路径?从一个岛屿开始,一圈一圈往外扩(BFS),直到扩展到第二座岛 。

遍历的思路类似于上题的最大人工岛,将遍历过的岛屿标记成“2”来做区分,而不是简单地淹掉。  

class Solution {
public:
    int shortestBridge(vector<vector<int>>& grid) {
        vector<vector<int>> dire={{1,0},{0,1},{-1,0},{0,-1}};
        queue<pair<int,int>> q;//队列中装的是各点的坐标
        int n=grid.size();
        int count=1;
        for(int i=0;i<n;i++)//只遍历一座岛屿,做队列的初始化
        {
            for(int j=0;j<n;j++)
            {
                if(grid[i][j]==1&&count>0)
                {
                    dfs(grid,q,i,j);
                    count--;
                }
            }
        }
        int step=0;
        while(!q.empty())
        {
            int size=q.size();
            for(int i=0;i<size;i++)//让岛屿周围的一圈海都向外扩展
            {
                auto [x,y]=q.front();//用队头的pair赋给auto [x,y],就不需要用first和second
                q.pop();
                if(grid[x][y]==1)//最终结果的条件判断
                {
                    return step;
                }
                for(int j=0;j<dire.size();j++)//从一个点向四个方向扩散(做选择)
                {
                    int xn=x;
                    int yn=y;
                    xn+=dire[j][0];
                    yn+=dire[j][1];
                    if(xn<0||yn<0||xn>=n||yn>=n)
                    {
                        continue;    
                    }
                    if(grid[xn][yn]==0)//将周围的一圈海标记为2
                    {
                        grid[xn][yn]=2;
                        q.emplace(xn,yn);
                    }
                    if(grid[xn][yn]==1)//扩散中接触到了陆地,最短路径为step+1
                        return step+1;
                }
            }
            step++;
        }
        return -1;
    }   
    //深度优先遍历第一座岛,将岛屿及其周边的一圈都标记为 2
    //将岛屿周边的一圈海的位置(i,j)压入队列中,做队列的初始化
    void dfs(vector<vector<int>>& grid,queue<pair<int,int>>& q,int i,int j)
    {
        int n=grid.size();
        if(i<0||j<0||i>=n||j>=n)
        {
            return ;
        }
        if(grid[i][j]==0)//岛屿周边的一圈海
        {
            grid[i][j]=2;//标记为 2
            q.emplace(i,j);//将其位置(i,j)压入队列中
            return ;
        }
        if(grid[i][j]==2)//遇到已经遍历过的,直接返回
            return ;
        grid[i][j]=2;//将遍历到的岛屿标记为 2
        dfs(grid,q,i+1,j);
        dfs(grid,q,i,j+1);
        dfs(grid,q,i-1,j);
        dfs(grid,q,i,j-1);
    }
};
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以帮助您写一个深度优先遍历算法来解决这个问题。 首先,让我们定义一个函数来计算一个给定的二维数组最大的岛屿面积: ```python def max_island_area(grid): max_area = 0 for i in range(len(grid)): for j in range(len(grid[0])): if grid[i][j] == 1: area = dfs(grid, i, j) max_area = max(max_area, area) return max_area ``` 在这个函数,我们迭代整个二维数组,找到每个陆地块(即 grid[i][j] == 1),然后对于每个陆地块,我们运行深度优先遍历算法来计算与之相连的陆地块的数量。 接下来,我们来定义深度优先遍历算法 dfs: ```python def dfs(grid, i, j): if i < 0 or i >= len(grid) or j < 0 or j >= len(grid[0]) or grid[i][j] == 0: return 0 grid[i][j] = 0 area = 1 area += dfs(grid, i+1, j) area += dfs(grid, i-1, j) area += dfs(grid, i, j+1) area += dfs(grid, i, j-1) return area ``` 在这个算法,我们首先检查当前坐标是否越界或已经被访问过(即 grid[i][j] == 0),如果是这样,就返回 0。否则,我们将当前坐标的值置为 0,表示已经被访问过。然后,我们递归地遍历当前坐标的上下左右四个方向,并将每个方向的面积相加,最后返回总面积。 最后,我们可以使用下面的代码来测试我们的算法: ```python grid = [[1, 1, 0, 0, 0], [1, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, 1]] print(max_island_area(grid)) # 输出:4 ``` 在这个例子,我们的二维数组表示了一个 4x5 的海域,其最大的岛屿面积为 4。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值