【C++】BFS解决Floodfill问题

news2024/9/22 15:25:53

目录

Floodfill算法介绍

解决方法

BFS

图画渲染

算法思路:

代码实现:

岛屿数量

算法思路

代码实现

岛屿的最大面积 

算法思想

代码实现

被围绕的区域

算法思路

代码实现

总结:


Floodfill算法介绍

Floodfill翻译过来就是“洪水灌溉”;什么事洪水灌溉呢?先来看一幅图;

图中的数字代表的是陆地的高度,水往地处流,这里我们用负数代表低洼的区域,所以这个图中可以分为陆地和水域两种区域,水域是被陆地包裹着的;

洪水来了之后,绿色的部分就会被填充;

那么洪水来了之后,我们应该如何操作才能将低洼的地区填满(处理)呢?

解决方法

我们可以使用DFS和BFS来解决此类问题,本章博客我分享下BFS的解决方法;

BFS

BFS即Breadth First Search,即广度优先搜索。如果说DFS是一条路走到黑的话,BFS就完全相反了。BFS会在每个岔路口都各向前走一步。以二叉树为例;

与二叉树的层序遍历无出其右,一层一层的进行处理数据,每次只走一步;实现BFS的关键就是使用队列;

 BFS的代码实现其实也是有模版的,接下来我们来解决几道例题,心中自然就会明了了;

图画渲染

例题地址:. - 力扣(LeetCode)

算法思路:

这道题的思路很简单,题给出了一个初始的坐标,然后叫我们把这块区域周围与此坐标的值相等的连通区域改成目标颜色;
这里把image[sr][sv]赋值给pre;color是要更改的最终的颜色;
思路:如果给的pre处的颜色与要更改的颜色相同,那么就不需要操作;因为我们要更改的就是pre周围与他相同颜色的区域;如果pre处的颜色不是color,那么我们就需要通过BFS来把周围颜色与pre处相同的区域更改成color;
2.在floodfill函数中我们只需要调用一次BFS即可;
3.实现BFS算法:
a.传参我们需要把数组和坐标传过去,这道题还需要传递color;
b.m,n分别为数组中的行和列,用与边界的判断;
遍历技巧:定义两个数组dx和dy;这两个数组是用来就是遍历的,就下面的数组来看坐标x=a+dx[i],y=b+dy[i];进行四次遍历,dx,dy分别是(0,1)、(0,-1)、(1,0)、(-1,0)就是分别访问了以(a,b)为中心的上下左右四个方向;
visit数组是用来进行标记访问过区域的;因为有的情况前面访问过的区域还可能会被再次访问,为了避免这种情况我们需要进行标记;
c.因为我们之后要把pre处颜色更改,遍历需要与之前的颜色进行对比,所以我们使用tmp记下来pre处的颜色;如果pre处的颜色是color直接返回即可;
d.构建个队列q,然后把pre压进去,再把pre的颜色更改为color,在标记下访问过了;
e.使用while循环,接下来就是层序遍历的代码模版了,这道题的核心就是遍历然后标记更改颜色;

代码实现:

class Solution {
public:

    int dx[4]={0,0,1,-1};
    int dy[4]={ 1,-1,0,0};
    bool visit[60][60]; 
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
        bfs(image,sr,sc,color);
        return image; 
    }
    void  bfs(vector<vector<int>>& image, int sr, int sc, int color)
    {
        int m=image.size();int n=image[0].size();
        int tmp=image[sr][sc];
        if(image[sr][sc]!=color)
        {
            image[sr][sc]=color;
            visit[sr][sc]=true; 
        }
        queue<pair<int,int>>q;
        q.push({sr,sc});
        while(q.size())
        {
            auto [a,b]=q.front();
            q.pop();
            for(int i=0;i<4;i++)
            {
                int x=a+dx[i];int y=b+dy[i];//上下左右进行遍历
                if(x>=0&&x<m&&y>=0&&y<n&&!visit[x][y]&&image[x][y]==tmp)//边界判断
                {
                    q.push({x,y});//能进来的都是合法的坐标
                    image[x][y]=color;//更改颜色
                    visit[x][y]=true;//标记访问过了
                }
            }
        }
    }

};

岛屿数量

例题地址:. - 力扣(LeetCode)

算法思路

这道题与上一道题类似,我们只要找到‘1’,然后使用BFS将这联通块+处理一下(避免重复),每次找到一块岛屿就count++;最后返回即可;有的答案修改了原数组的值,这是一个接口函数,如果是在项目中最好不要修改原数组;保证函数的功能正确运行。

vis数组标记访问过的合法区域;dx、dy用来遍历

1.在numIslands函数中对二维数组进行扫描,如果遇到了一个‘1’,count++;然后就调用BFS把这块联通的区域标记一下,也就是把整个岛屿标记了、已经统计过了;m,n是数组的行和列;
2.老一套了,使用层序遍历,创建队列,压进去,标记,遍历,把合法的都标记下,塞进队列里;

代码实现

class Solution {
public:
 int dx[4] = {1, -1, 0, 0};
 int dy[4] = {0, 0, 1, -1};
    bool  vis[301][301];
    int m,n;
    int numIslands(vector<vector<char>>& grid) {
        int ret=0;
         m=grid.size(); n=grid[0].size();
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(grid[i][j]=='1'&&vis[i][j]==false)
                {
                    ret++;
                    bfs(grid,i,j);
                }
            }
        }
        return ret;
    }

    void bfs(vector<vector<char>>& grid,int i,int j)
    {
        queue<pair<int,int>>q;
        q.push({i,j});
        vis[i][j]=true;
        while(q.size())
        {
            auto [a,b]=q.front();

            q.pop();
            for(int k=0;k<4;k++)
            {
                int x=a+dx[k];int y=b+dy[k];
                if(x>=0&&x<m&&y>=0&&y<n&&grid[x][y]=='1'&&!vis[x][y])
                {
                    q.push({x,y});
                    vis[x][y]=true;//标记岛屿土地
                }
            }    
        }       
    }
};

岛屿的最大面积 

例题地址:. - 力扣(LeetCode)

算法思想

这道题找出最大的岛屿面积,在maxAreaOfIsland函数中,我们只需要扫描数组,找到‘1’,就调用BFS,并进行比较取较大的;
BFS实现:老套路,将这一联通块标记,在标记的同时要计算面积;每次向队列中插入一个数据就count++;最后返回面积;

代码实现

class Solution {
public:
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    int m,n;
    bool vis[51][51];
    int maxAreaOfIsland(vector<vector<int>>& grid) {
         m=grid.size(); n=grid[0].size();
        int ret=0;
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(grid[i][j]==1&&!vis[i][j])
                ret=max(ret,bfs(grid,i,j));   
            }
        }
        return ret;
    }
    int bfs(vector<vector<int>>& grid,int i,int j)
    {
        queue<pair<int,int>>q;
        int count=1;
        q.push({i,j});
        vis[i][j]=true;
        while(q.size())
        {
            auto [a,b]=q.front();
            q.pop();
            for(int k=0;k<4;k++)
            {
                int x=a+dx[k];
                int y=b+dy[k];
                if(x>=0&&x<m&&y>=0&&y<n&&grid[x][y]==1&&!vis[x][y])
                {
                    q.push({x,y});
                    vis[x][y]=true;
                    count++;
                }
            }
        }
        return count;
    }
};

被围绕的区域

例题地址:. - 力扣(LeetCode)

算法思路

这道题我们正着做比较难,但是我们可以反着做,先把边界的O的联通块通过BFS更改成一个无关字符,这里我更改为点,然后我们在进行原数组中的扫描,如果遇到‘O’,那就修改为‘X’,遇到‘点’就改成‘O’;

BFS实现:m,n数组的行和列,dx,dy用来遍历,开一个队列,一层一层的把合法的插入进去,同时更改成‘点’;

代码实现

class Solution {
public:
    int m,n;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    void solve(vector<vector<char>>& board) {
        m=board.size();n=board[0].size();
        //现将边缘的进行处理
        for(int i=0;i<m;i++)
        {
            if(board[i][0]=='O')bfs(board,i,0);
            if(board[i][n-1]=='O')bfs(board,i,n-1);
        }
        for(int i=0;i<n;i++)
        {
            if(board[0][i]=='O')bfs(board,0,i);
            if(board[m-1][i]=='O')bfs(board,m-1,i);
        }
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(board[i][j]=='O')
                {
                    board[i][j]='X';
                }
                else if(board[i][j]=='.')
                {
                    board[i][j]='O';
                }
            }
        }
        return ;
    }
    
    void bfs(vector<vector<char>>& board,int i,int j)
    {
        queue<pair<int,int>>q;
        q.push({i,j});
        board[i][j]='.';
        while(q.size())
        {
            auto [a,b]=q.front();
            q.pop();
            for(int k=0;k<4;k++)
            {
                int x=a+dx[k];int y=b+dy[k];
                if(x>=0&&x<m&&y>=0&&y<n&&board[x][y]=='O')
                {
                    q.push({x,y});
                    board[x][y]='.';
                }
            }
        }
        return ;
    }
};

总结:

BFS框架是层序遍历(队列实现),核心在于遍历内部的代码,根据题目进行处理,另外就是遍历的技巧十分的方便。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1990195.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

serial靶机渗透~反序列化

反序列化又叫对象注入&#xff0c;序列化在内部没有漏洞&#xff0c;漏洞产生是因为程序在处理对象、魔术函数以及序列化相关的问题导致的&#xff0c;当传给 unserialize()的参数可控时&#xff0c;那么用户就可以注入 payload&#xff0c;进行反序列化的时候就可能触发对象中…

【iOS】AutoreleasePool自动释放池的实现原理

目录 ARC与MRC项目中的main函数自动释放池autoreleasepool {}实现原理AutoreleasePoolPage总结 objc_autoreleasePoolPush的源码分析autoreleaseNewPageautoreleaseFullPageautoreleaseNoPage autoreleaseFast总结 autorelease方法源码分析objc_autoreleasePoolPop的源码分析po…

Html详解——Vue基础

HTML是什么&#xff1f; 超文本标记语言&#xff08;英语&#xff1a;HyperText Markup Language&#xff0c;简称&#xff1a;HTML&#xff09;是一种用来结构化 Web 网页及其内容的标记语言。网页内容可以是&#xff1a;一组段落、一个重点信息列表、也可以含有图片和数据表…

山海关古城信息管理测试--片区

1.片区的检验名称编号是否重复 1.1controller添加两个方法&#xff0c;检验片区编号和检验片区名称 作用为&#xff1a;调用方法判断片区编号与片区名称是否重复&#xff0c;并返回返回值 /*** 检验片区编号是否重复*/PostMapping( "/checkPqbhUnique")ResponseBody…

深度解密CRLF注入与重定向漏洞:从原理到实践

在网络安全的世界中&#xff0c;CRLF注入和重定向漏洞常常被视为潜在的威胁&#xff0c;可能导致信息泄露和用户误导等严重后果。CRLF注入利用换行符在HTTP响应中插入恶意代码&#xff0c;而重定向漏洞则可能将用户引导至恶意网站。理解这些漏洞的原理及其复现方法&#xff0c;…

一文了解服务器和电脑主机的区别及各自优势

服务器和电脑主机的区别主要是&#xff1a;服务器专为处理大量数据和网络服务设计&#xff0c;具备高性能、高稳定性和可扩展性&#xff0c;通常用于数据中心或大型企业环境&#xff1b;而电脑主机则面向个人用户&#xff0c;主要用于日常办公、娱乐等通用任务&#xff0c;成本…

【QT】Qt中Websocket的使用

一、WebSocket的定义 WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455&#xff0c;并由RFC7936补充规范。WebSocket API也被W3C定为标准。 WebSocket使得客户端和服务器之间的数据交换变得更加简单&#xff0c;…

HelloWorld驱动编写和加载驱动实验

HelloWorld驱动编写和加载驱动实验 Helloworld驱动实验驱动编写驱动的基本框架 内核模块实验设置交叉编译器找到RK3568平台交叉编译器&#xff1a;解压交叉编译器&#xff1a;设置全局的交叉编译器环境验证交叉编译器环境 编写Makefile编译模块模块的加载与卸载查看模块信息 He…

WT2605C蓝牙语音芯片赋能对讲机新体验:无屏操控、音频解码与蓝牙音箱三合一

一、产品概况 对讲机市场是一个技术成熟且具有广泛应用前景的市场。对讲机作为无线通信设备的一种&#xff0c;在许多不同的领域和业务中发挥着重要作用。从技术发展角度来看&#xff0c;对讲机经历了从模拟到数字的转型&#xff0c;以及从简单通信工具向多功能设备的演进。当…

LVS实验——部署DR模式集群

目录 一、实验环境 二、配置 1、LVS 2、router 3、client 4、RS 三、配置策略 四、测试 1.Director服务器采用双IP桥接网络&#xff0c;一个是VPP&#xff0c;一个DIP 2.Web服务器采用和DIP相同的网段和Director连接 3.每个Web服务器配置VIP 4.每个web服务器可以出外网…

【Python机器学习】回归——缩减系数来“理解”数据

如果数据特征比样本点还多&#xff0c;是不可以使用线性回归的&#xff0c;因为在计算的时候会出错。 如果特征比样本点还多&#xff08;n>m&#xff09;&#xff0c;也就是说输入数据的矩阵x不是满秩矩阵。非满秩矩阵在求逆时会出问题。 为了解决上述问题&#xff0c;可以…

贪心算法的初涉(双指针 + “过山车思想”)

“过山车”思想 首先我们用一道力扣的题目&#xff0c;来简单了解一下“过山车思想” 3228. 将 1 移动到末尾的最大操作次数 - 力扣&#xff08;LeetCode&#xff09; 给你一个 二进制字符串 s。 你可以对这个字符串执行 任意次 下述操作&#xff1a; 选择字符串中的任一…

京东京造的C2M供应链模式

京东自有品牌业务于2018年1月正式上线&#xff0c;在京东发展已久&#xff0c;依托京东供应链优势&#xff0c;已搭建出京东京造、惠寻、佳佰等多品牌矩阵。 京东给零售企业释放出了一个讯号&#xff1a;C2M崛起&#xff0c;消费者的需求开始走向多元化和个性化&#xff01; …

徐州市委书记宋乐伟一行莅临非凸科技徐州分公司调研

7月23日&#xff0c;徐州市委书记宋乐伟一行莅临非凸科技徐州分公司调研&#xff0c;详细了解非凸科技数智交易产品的生态体系以及AI算力赋能的实践成果&#xff0c;并就相关工作进行了现场指导与交流。 非凸科技徐州分公司位于淮海路经济区金融服务中心云盛大厦&#xff0c;致…

基于JSP、java、Tomcat三者的项目实战--校园交易平台系统--(实习,答辩皆可用到)--万字爆更

技术支持&#xff1a;JAVA、JSP 服务器&#xff1a;TOMCAT 7.0.86 编程软件&#xff1a;IntelliJ IDEA 2021.1.3 x64 全部文件展示 网页实现功能截图 主页 注册 登录 购物车主页 修改功能 修改成功 添加商品功能 添加成功 添加进入购物车功能 支付功能 支付过的历史清单账单…

Comsol 声固耦合条件下超长水管路声传递损失

声固耦合条件指的是声波在固体和液体之间传递时&#xff0c;两者之间存在接触或耦合的情况。在水管路中&#xff0c;声固耦合条件下的声传递损失可以通过以下几个因素来影响和计算&#xff1a; 1. 声波的反射和透射&#xff1a;当声波从一个介质传递到另一个介质时&#xff0c…

服务器 Linux 的网络信息

博主上回大致讲解了文件系统&#xff0c;今天来说说 Linux 的网络信息&#xff0c;还是比较重要的~ 主机名称 临时修改 hostname node01 永久修改 vi /etc/hostname DNS解析 域名解析服务可以将域名转换为IP地址DNS域名劫持 window --> C:\Windows\System32\drivers…

Java 2.2 - Java 集合

Java 集合&#xff0c;也叫做容器&#xff0c;主要是由两大接口派生而来&#xff1a;一个是 Collection 接口&#xff0c;主要用于存放单一元素&#xff1b;另一个是 Map 接口&#xff0c;主要用于存放键值对。对于 Collection 接口&#xff0c;其下又有三个主要的子接口&#…

七大云安全威胁及其应对方法

关注公众号网络研究观获取更多内容。 对于任何依赖云来容纳快速增长的服务的企业来说&#xff0c;确保安全都是重中之重。然而&#xff0c;正如大多数云采用者很快意识到的那样&#xff0c;迁移到动态云环境需要新的和更新的安全措施&#xff0c;以确保数据和其他关键资产在整…

凯特王妃与戴安娜王妃:有跨越时空的优雅共鸣!

显而易见的是都是王妃,而王妃不仅是称谓也是一个头衔,她们同时要承担这个头衔应尽的职责! 在皇室世界里,总有一些名字,如同璀璨星辰,即便时光流转,依旧熠熠生辉。现在让我们揭开一段不为人知的幕后故事——凯特王妃与已故的戴安娜王妃之间,那些超越时代、共通的优雅与情…