首页 > 编程语言 >【图论#02】岛屿数量,flood fill算法的代码实现与优化

【图论#02】岛屿数量,flood fill算法的代码实现与优化

时间:2023-08-21 13:34:58浏览次数:40  
标签:02 int dfs flood grid dx dy row fill

岛屿数量

给你一个由 '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

思路

flood fill泛洪算法+dfs

首先我们使用两层for循环遍历二维数组grid,当遇到'1'的时候,标记遇到了“岛屿”,此时岛屿数量加1

然后触发dfs,先将当前位置(是'1')设为'0',在遇到'1'的位置的上下左右搜索。如果碰到'1',说明该位置仍是目前发现的岛屿的一部分陆地,将其改为'0',如果碰到'0'就不管,说明该位置是水不是陆地。

上述操作就是flood fill中的“同化”操作

dfs结束后,当前位置的上下左右都变成了'0'(包括当前位置本身),然后for循环会继续向后遍历,直到再次碰到'1'(即陆地),然后通过一样的方法统计陆地数量并使用dfs同化。

当二维数组grid遍历完成,那么岛屿的数量也就统计完成了。

疑问

以下是解答1,该代码可以通过测试

class Solution {
public:
    int row, col;
    int dx[4] = {-1,0,1,0};//如果记不住,可以假设一个(1,1),然后通过加减1来确定对应偏移量
    int dy[4] = {0,-1,0,1};
    int numIslands(vector<vector<char>>& grid) {
        int res = 0;
        row = grid.size();//行
        col = grid[0].size();//列
        for(int x = 0; x < row; ++x){//遍历grid,寻找陆地'1'
            for(int y = 0; y < col; ++y){
                if(grid[x][y] == '1'){//找到陆地之后,同化其上下左右四个方向的区域为'0'
                    dfs(grid, x, y);//调用递归实现
                    res++;//记录岛屿数量
                }
            }
        }
        return res;
    }

    void dfs(vector<vector<char>>& grid, int x, int y){
        grid[x][y] = '0'; 
        for(int i = 0; i < 4; ++i){//循环四次,使用偏移量计算出当前位置的上下左右位置的坐标
            int nx = x + dx[i];
            int ny = y + dy[i];
            if(nx >= 0 && nx < row && ny >= 0 && ny < col && grid[nx][ny] == '1'){
                dfs(grid, nx, ny);//这里其实已经给递归设置了结束条件,如果不满足上面的if的话是不会进入递归的
            }
        }
    }
};

下面是我的解答代码,我也使用了同样的思想,但是为什么我的代码会超时

#include <ratio>
class Solution {
public:
    void dfs(vector<vector<char> >& grid, int x, int y, int row, int col){
        //递归终止条件
        if(x < 0 || x >= row || y < 0 || y >= col || grid[x][y] == '0') return;

        //单层处理逻辑
        grid[x][y] = '0';
        dfs(grid, x - 1, y, row, col);
        dfs(grid, x + 1, y, row, col);
        dfs(grid, x, y - 1, row, col);
        dfs(grid, x, y + 1, row, col);
        return;
    }
    int solve(vector<vector<char> >& grid) {
        // write code here
        if(grid.size() == 0) return 0;
        int row = grid.size();
        int col = grid[0].size();
        int res = 0;
        for(int x = 0; x < row; ++x){
            for(int y = 0; y < col; ++y){
                if(grid[x][y] == '1'){
                    res += 1;
                    dfs(grid, x, y, row, col);
                }
            }
        }
        return res;
    }
};

原因

这个主要是代码实现的问题,思路是一样的,都是基于flood fill泛洪算法+dfs实现

我的代码中,递归中遍历的东西太多,加上递归深度比较深,所以出现了栈溢出的问题

例如,在实现flood fill中"同化"操作时,我的代码需要对上下左右进行四次递归才能将所有情况尝试一遍,这就产生了巨大的开销

优化

使用偏移量来简化对四个方向的搜索

dxdy数组来表示上下左右四个方向的偏移量。通过对当前位置 (x, y) 分别加上 dx[i]dy[i],可以得到该方向的相邻位置 (nx, ny)

		for(int i = 0; i < 4; ++i){
            int nx = x + dx[i];//由当前位置坐标加上偏移量计算得到的上下左右的坐标
            int ny = y + dy[i];
            if(nx >= 0 && nx < row && ny >= 0 && ny < col && grid[nx][ny] == '1'){
                dfs(grid, nx, ny);
            }
        }

这样优化之后,我们只需要循环4次,计算四个方向的坐标,然后每次调用一个递归函数就可以实现对四个方向的搜索

减少了递归深度

举个例子来说明,假设当前位置为 (1, 1),则根据 dxdy 数组中的元素,可以得到下面四个相邻位置:

  • 上方位置:(1 + dx[0], 1 + dy[0]) = (1 - 1, 1 + 0) = (0, 1)
  • 左方位置:(1 + dx[1], 1 + dy[1]) = (1 + 0, 1 - 1) = (1, 0)
  • 下方位置:(1 + dx[2], 1 + dy[2]) = (1 + 1, 1 + 0) = (2, 1)
  • 右方位置:(1 + dx[3], 1 + dy[3]) = (1 + 0, 1 + 1) = (1, 2)

可以看到,通过不同的 dx[i]dy[i] 组合,可以分别表示上下左右四个方向的移动。

其中,dx[0] = -1 代表向上移动,dx[1] = 0 代表向左移动,dx[2] = 1 代表向下移动,dx[3] = 0 代表向右移动。

类似地,dy[0] = 0dy[1] = -1dy[2] = 0dy[3] = 1 分别对应着上下左右四个方向的纵向偏移量。

标签:02,int,dfs,flood,grid,dx,dy,row,fill
From: https://www.cnblogs.com/DAYceng/p/17645765.html

相关文章

  • 在 Amazon Linux 2023 上托管 WordPress 博客
    以下步骤将帮助您在AmazonLinux2023实例上安装、配置和保护WordPress博客。本教程是很好的AmazonEC2入门教程,因为您可以完全控制托管您WordPress博客的Web服务器,这对传统的托管服务来说并不是一个典型的方案。您负责更新软件包并为您的服务器维护安全补丁。对于不需......
  • python刷小红书流量(小眼睛笔记访问量),metrics_report接口,原理及代码,以及x-s签名验证202
    一、什么是小眼睛笔记访问量 如下图所示,为笔记访问量。二、小眼睛笔记访问量接口1、urlhttps://edith.xiaohongshu.com/api/sns/web/v1/note/metrics_report2、payloaddata={"note_id":note_id,"note_type":note_type,"report_type":1,......
  • CSP-J2022 复盘
    T1乘方方法\(1\):使用快速幂,判断答案是否\(\geq10^9\)。方法\(2\):特判\(1\)的情况,其余的可以直接乘。此处给出方法\(2\)的代码。#include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;lla,b;intmain(){ cin>>a>>b; if(a==1)cout<<1; else{ ......
  • 在 Amazon Linux 2023 上安装 LAMP
    通过以下步骤,您可以将带PHP和 MariaDB(一个由社区开发的MySQL分支)支持的ApacheWeb服务器(有时称为LAMPWeb服务器或LAMP堆栈)安装到AmazonLinux2023实例上。您可以使用此服务器来托管静态网站或部署能对数据库中的信息执行读写操作的动态PHP应用程序。重要这......
  • 前端学习笔记202308学习笔记第七拾玖天-Map之2
    ......
  • YACS 2023年8月月赛 乙组 T3 香槟塔 题解
    题目链接乙组中比较好的一道思维题。首先考虑暴力,如果没满就倒满了就往下继续倒,直到倒完或溢出为止,但如果开始就全满然后每次都从最上面倒那么$O(n^2)$就超时了。我们希望找到一个数据结构(当然不是也行)能够快速得到从某个位置向下(包括当前位置)第一个没满的香槟塔,显然并查集。......
  • YACS 2023年8月月赛 乙组 T1 最长回文 题解
    题目链接小清新的区间DP题。看到数据范围以及回文一眼盯真得到是区间DP。设$f[i][j]$为区间$[i,j]$成为回文串最少要经过几次操作,转移一个个看。首先可以删掉第$j$个,$f[i][j]=\min(f[i][j],f[i][j-1]+1)$,同理也可以删掉第$i$个,$f[i][j]=\min(f[i][j],f[i+1][j]+1)$......
  • 带你来吃瓜!Andy Pavlo教授带您一文回顾数据库的2022年
    theme:fancy编辑/翻译:宇亭校对:王学姣、李浩本文是由数据库界知名专家AndyPavlo教授写的2022年数据库回顾文章,这个系列从去年开始,非常经典,也比较系统的整理了一下数据库界的大事件(当然,主要还是以国外的居多),StoneDB团队对本文进行了翻译,小编在一些链接部分加了注释,方便大家......
  • YACS 2023年6月月赛 乙组 T3 工作安排 题解
    这道题是乙组里比较新奇的一题,本来一眼看下来不会,后来蒙了个按照单位时间内收到罚款排序居然对了,十分意外。简单的证明一下:假设有两个工作,时间分别为$t_1$$f_1$$t_2$$f_2$,假设把第一个放在前面更优,前面的罚款不变。则有$t_1\timesf_1+(t_1+t_2)\timesf_2<t_2\timesf_2+(......
  • Visual Studio 2022 实用调试技巧
    1、什么是bug?bug本意是昆⾍”或“⾍⼦”,现在⼀般是指在电脑系统或程序中,隐藏着的⼀些未被发现的缺陷或问题,简称程序漏洞。“Bug”的创始⼈格蕾丝·赫柏(GraceMurrayHopper),她是⼀位为美国海军⼯作的电脑专家,1947年9⽉9⽇,格蕾丝·赫柏对HarvardMarkII设置好17000个继电器进⾏......