算法沉淀——BFS 解决 FloodFill 算法(leetcode真题剖析)

算法沉淀——BFS 解决 FloodFill 算法
- 01.图像渲染
- 02.岛屿数量
- 03.岛屿的最大面积
- 04.被围绕的区域
BFS(广度优先搜索)解决 Flood Fill 算法的基本思想是通过从起始点开始,逐层向外扩展,访问所有与起始点相连且具有相同特性(颜色等)的区域。在 Flood Fill 中,通常是通过修改图像的像素颜色。
下面是 BFS 解决 Flood Fill 算法的步骤:
- 初始化: 将起始点的颜色修改为新的颜色,将起始点加入队列。
- BFS 遍历: 使用队列进行 BFS 遍历。每次从队列中取出一个位置,检查其相邻的位置是否符合条件(与起始点颜色相同),如果符合,则修改颜色并将其加入队列。这样,不断扩展遍历。
- 遍历直到完成: 重复上述步骤,直到队列为空,即没有可继续扩展的位置为止。此时,所有与起始点相连的区域都被成功修改。
在 Flood Fill 中,BFS 保证了相邻区域的逐层遍历,确保了所有相连的、颜色相同的区域都被填充为新的颜色。
01.图像渲染
题目链接:https://leetcode.cn/problems/flood-fill/
有一幅以 m x n 的二维整数数组表示的图画 image ,其中 image[i][j] 表示该图画的像素值大小。
你也被给予三个整数 sr , sc 和 newColor 。你应该从像素 image[sr][sc] 开始对图像进行 上色填充 。
为了完成 上色工作 ,从初始像素开始,记录初始坐标的 上下左右四个方向上 像素值与初始坐标相同的相连像素点,接着再记录这四个方向上符合条件的像素点与他们对应 四个方向上 像素值与初始坐标相同的相连像素点,……,重复该过程。将所有有记录的像素点的颜色值改为 newColor 。
最后返回 经过上色渲染后的图像 。
示例 1:

输入: image = [[1,1,1],[1,1,0],[1,0,1]],sr = 1, sc = 1, newColor = 2
输出: [[2,2,2],[2,2,0],[2,0,1]]
解析: 在图像的正中间,(坐标(sr,sc)=(1,1)),在路径上所有符合条件的像素点的颜色都被更改成2。
注意,右下角的像素没有更改为2,因为它不是在上下左右四个方向上与初始点相连的像素点。
示例 2:
输入: image = [[0,0,0],[0,0,0]], sr = 0, sc = 0, newColor = 2
输出: [[2,2,2],[2,2,2]]
思路
这个题我们可以使用最朴素的bfs遍历来解决。
代码
class Solution {const int dx[4] = {0, 0, 1, -1}; // 表示上、下、左、右四个方向的相对坐标变化const int dy[4] = {-1, 1, 0, 0};
public:vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {int prev = image[sr][sc]; // 记录起始位置的颜色if (prev == color) return image; // 如果新旧颜色相同,不需要进行填充int m = image.size(), n = image[0].size();queue<pair<int, int>> q;q.push({sr, sc});while (!q.empty()) {auto [a, b] = q.front();q.pop();image[a][b] = color; // 修改当前位置的颜色for (int i = 0; i < 4; ++i) {int x = a + dx[i], y = b + dy[i];// 判断新的坐标是否越界,并且颜色与旧颜色相同if (x >= 0 && x < m && y >= 0 && y < n && image[x][y] == prev) {q.push({x, y});}}}return image;}
};
- 初始化: 记录起始位置的颜色
prev,如果起始颜色和目标颜色相同,直接返回原图。 - BFS 遍历: 使用队列
q进行 BFS 遍历。从起始位置开始,逐层遍历相邻位置,将颜色修改为目标颜色。 - 遍历直到完成: 重复上述步骤,直到队列为空,即没有可继续扩展的位置为止。此时,所有与起始点相连的区域都被成功修改为新的颜色。
02.岛屿数量
题目链接:https://leetcode.cn/problems/number-of-islands/
给你一个由 '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
提示:
m == grid.lengthn == grid[i].length1 <= m, n <= 300grid[i][j]的值为'0'或'1'
思路
使用bfs思想遍历每一个方格,将一块遍历过的岛屿全都标记为海洋也就是0,或者使用同等大小的数组进行遍历的标记。
代码
class Solution {const int dx[4] = {0, 0, 1, -1}; // 表示上、下、左、右四个方向的相对坐标变化const int dy[4] = {1, -1, 0, 0};int m, n; // m表示行数,n表示列数queue<pair<int, int>> q; // 用于BFS的队列// BFS函数,从起点 (i, j) 开始遍历并标记属于同一岛屿的所有位置void bfs(vector<vector<char>>& grid, int i, int j) {q.push({i, j}); // 将起点入队grid[i][j] = '0'; // 标记已经遍历过的位置while (!q.empty()) {auto [a, b] = q.front();q.pop();for (int k = 0; k < 4; ++k) {int x = a + dx[k], y = b + dy[k];// 判断新的坐标是否越界,并且是岛屿的一部分if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == '1') {q.push({x, y}); // 将相邻的岛屿位置入队grid[x][y] = '0'; // 标记已经遍历过的位置}}}}public:int numIslands(vector<vector<char>>& 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') {ret++; // 发现新的岛屿,增加计数bfs(grid, i, j); // 使用BFS遍历并标记所有属于同一岛屿的位置}}}return ret; // 返回岛屿数量}
};
- 初始化: 定义了方向数组
dx和dy,表示上、下、左、右四个方向的相对坐标变化。初始化队列q,用于BFS遍历。 - BFS遍历: 对于每个未被访问的岛屿起点,调用
bfs函数进行BFS遍历。在BFS过程中,将属于同一岛屿的位置标记为已访问。 - 遍历整个网格: 使用两层循环遍历整个网格,如果发现未访问过的岛屿起点,就调用
bfs函数进行遍历,并增加岛屿数量计数。 - 返回结果: 最终返回岛屿的数量。
03.岛屿的最大面积
给你一个大小为 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 。
示例 2:
输入:grid = [[0,0,0,0,0,0,0,0]]
输出:0
提示:
m == grid.lengthn == grid[i].length1 <= m, n <= 50grid[i][j]为0或1
思路
总体和上面的题目解法一样,只不过我们在遍历每个岛屿时,顺便计算个数即岛屿的面积,每次计算完再比较,最后返回最大的岛屿面积。
代码
class Solution {const int dx[4] = {0, 0, 1, -1}; // 上、下、右、左四个方向的相对坐标变化const int dy[4] = {-1, 1, 0, 0};queue<pair<int, int>> q; // 用于BFS的队列int m, n; // m 表示行数,n 表示列数// BFS 函数,从起点 (i, j) 开始遍历并标记属于同一岛屿的所有位置int bfs(vector<vector<int>>& grid, int i, int j) {int count = 1; // 记录岛屿的大小q.push({i, j}); // 将起点入队grid[i][j] = 0; // 标记已经遍历过的位置while (!q.empty()) {auto [a, b] = q.front();q.pop();for (int k = 0; k < 4; ++k) {int x = a + dx[k], y = b + dy[k];// 判断新的坐标是否越界,并且是岛屿的一部分if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1) {grid[x][y] = 0; // 标记已经遍历过的位置count++; // 增加岛屿的大小q.push({x, y}); // 将相邻的岛屿位置入队}}}return count;}public:int maxAreaOfIsland(vector<vector<int>>& 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) {ret = max(ret, bfs(grid, i, j)); // 计算并更新最大岛屿面积}}}return ret; // 返回最大岛屿面积}
};
- 初始化: 定义了方向数组
dx和dy,表示上、下、左、右四个方向的相对坐标变化。初始化队列q,用于BFS遍历。 - BFS遍历: 对于每个未被访问的岛屿起点,调用
bfs函数进行BFS遍历。在BFS过程中,将属于同一岛屿的位置标记为已访问,并统计岛屿的大小。 - 遍历整个网格: 使用两层循环遍历整个网格,如果发现未访问过的岛屿起点,就调用
bfs函数进行遍历,并计算并更新最大岛屿面积。 - 返回结果: 最终返回最大岛屿面积。
04.被围绕的区域
题目链接:https://leetcode.cn/problems/surrounded-regions/
给你一个 m x n 的矩阵 board ,由若干字符 'X' 和 'O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。
示例 1:

输入:board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]]
输出:[["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]]
解释:被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O' 都不会被填充为 'X'。 任何不在边界上,或不与边界上的 'O' 相连的 'O' 最终都会被填充为 'X'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
示例 2:
输入:board = [["X"]]
输出:[["X"]]
提示:
m == board.lengthn == board[i].length1 <= m, n <= 200board[i][j]为'X'或'O'
思路
这里我们可以使用bfs先处理所有与边界有关的位置,把它修改成其他字符,再依次遍历,留在格子中的字母O就是需要被修改的,而改成其他字符的就可以改回字母O。
代码
class Solution {const int dx[4] = {0, 0, 1, -1}; // 上、下、右、左四个方向的相对坐标变化const int dy[4] = {-1, 1, 0, 0};int m, n; // m 表示行数,n 表示列数queue<pair<int, int>> q; // 用于BFS的队列// BFS 函数,从起点 (i, j) 开始遍历并标记属于同一区域的所有位置void bfs(vector<vector<char>>& board, int i, int j) {q.push({i, j}); // 将起点入队board[i][j] = '#'; // 标记已经遍历过的位置while (!q.empty()) {auto [a, b] = q.front();q.pop();for (int k = 0; k < 4; ++k) {int x = a + dx[k], y = b + dy[k];// 判断新的坐标是否越界,并且是未被围绕的区域if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'O') {board[x][y] = '#'; // 标记已经遍历过的位置q.push({x, y}); // 将相邻的未被围绕的区域位置入队}}}}public:void solve(vector<vector<char>>& board) {m = board.size(); // 获取行数n = board[0].size(); // 获取列数// 对边界上的'O'进行BFS遍历,标记为'#'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 = 1; i < m - 1; ++i) {if (board[i][0] == 'O') bfs(board, i, 0);if (board[i][n - 1] == 'O') bfs(board, i, n - 1);}// 遍历整个网格,将未被标记的'O'修改为'X',已经标记的'#'修改回'O'for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (board[i][j] == '#') board[i][j] = 'O';else if (board[i][j] == 'O') board[i][j] = 'X';}}}
};
相关文章:
算法沉淀——BFS 解决 FloodFill 算法(leetcode真题剖析)
算法沉淀——BFS 解决 FloodFill 算法 01.图像渲染02.岛屿数量03.岛屿的最大面积04.被围绕的区域 BFS(广度优先搜索)解决 Flood Fill 算法的基本思想是通过从起始点开始,逐层向外扩展,访问所有与起始点相连且具有相同特性…...
wordpress外贸成品网站模板
首页大图slider轮播,橙色风格的wordpress外贸网站模板 https://www.zhanyes.com/waimao/6250.html 蓝色经典风格的wordpress外贸建站模板 https://www.zhanyes.com/waimao/6263.html...
如何使用六图一表七种武器
六图一表七种武器用于质量管理: 描述当遇到问题时应该用那张图来解决: 一、如果题目说出了质量问题需要找原因? 解:用因果图,因果图也称石川图或鱼骨图 二、如果要判断过程是否稳定受控? 解:…...
阿里云游戏服务器租用费用价格组成,费用详单
阿里云游戏服务器租用价格表:4核16G服务器26元1个月、146元半年,游戏专业服务器8核32G配置90元一个月、271元3个月,阿里云服务器网aliyunfuwuqi.com分享阿里云游戏专用服务器详细配置和精准报价: 阿里云游戏服务器租用价格表 阿…...
【C++】C++11上
C11上 1.C11简介2.统一的列表初始化2.1 {} 初始化2.2 initializer_list 3.变量类型推导3.1auto3.2decltype3.3nullptr 4.范围for循环5.final与override6.智能指针7. STL中一些变化8.右值引用和移动语义8.1左值引用和右值引用8.2左值引用与右值引用比较8.3右值引用使用场景和意义…...
【前端高频面试题--git篇】
🚀 作者 :“码上有前” 🚀 文章简介 :前端高频面试题 🚀 欢迎小伙伴们 点赞👍、收藏⭐、留言💬 前端高频面试题--git篇 往期精彩内容常用命令git add 和 git stage 有什么区别怎么使用git连接…...
c++创建对象
c创建对象 1.声明一个对象,然后使用默认构造函数来创建对象: class MyClass { public:MyClass() {// 构造函数代码} };int main() {MyClass obj; // 声明并创建一个对象return 0; }2.使用new和指针动态创建对象:不会自动释放 使用 new 运算…...
软件实例分享,洗车店系统管理软件会员卡电子系统教程
软件实例分享,洗车店系统管理软件会员卡电子系统教程 一、前言 以下软件教程以 佳易王洗车店会员管理软件V16.0为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 1、会员卡号可以绑定车牌号或手机号 2、卡号也可以直接使用手机号&a…...
【Docker进阶】镜像制作-用Dockerfile制作镜像(一)
进阶一 docker镜像制作 文章目录 进阶一 docker镜像制作用dockerfile制作镜像dockerfile是什么dockerfile格式为什么需要dockerfileDockerfile指令集合FROMMAINTAINERLABELCOPYENVWORKDIR 用dockerfile制作镜像 用快照制作镜像的缺陷: 黑盒不可重复臃肿 docker…...
数据密集型应用系统设计
数据密集型应用系统设计 原文完整版PDF:https://pan.quark.cn/s/d5a34151fee9 这本书的作者是少有的从工业界干到学术界的牛人,知识面广得惊人,也善于举一反三,知识之间互相关联,比如有个地方把读路径比作programming …...
分布式文件系统 SpringBoot+FastDFS+Vue.js【一】
分布式文件系统 SpringBootFastDFSVue.js【一】 一、分布式文件系统1.1.文件系统1.2.什么是分布式文件系统1.3.分布式文件系统的出现1.3.主流的分布式文件系统1.4.分布式文件服务提供商1.4.1.阿里OSS1.4.2.七牛云存储1.4.3.百度云存储 二、fastDFS2.1.fastDSF介绍2.2.为什么要使…...
【PyQt】11-QTextEdit、QPushButton
文章目录 前言一、文本输入-QTextEdit1.1 代码1.2 运行结果 二、QPushButton2.1.1 按钮上添加文本2.1.2 按键的弹跳效果2.1.3 两个信号可以绑定一个槽。2.1.4 带图标的按键运行结果 2.1.5 按键不可用以及回车默认完整代码2.2 单选按键控件运行结果 2.3 复选框(多选框…...
初识webpack(二)解析resolve、插件plugins、dev-server
目录 (一)webpack的解析(resolve) 1.resovle.alias 2.resolve.extensions 3.resolve.mainFiles (二) plugin插件 1.CleanWebpackPlugin 2.HtmlWebpackPlugin 3.DefinePlugin (三)webpack-dev-server 1.开启本地服务器 2.HMR模块热替换 3.devServer的更多配置项 (…...
什么是自编码器Auto-Encoder?
来源:https://www.bilibili.com/video/BV1Vx411j78H/?spm_id_from333.1007.0.0&vd_sourcef66cebc7ed6819c67fca9b4fa3785d39 为什么要压缩呢? 让神经网络直接从上千万个神经元中学习是一件很吃力的事情,因此通过压缩提取出原图片中最具代…...
openGauss学习笔记-219 openGauss性能调优-确定性能调优范围-硬件瓶颈点分析-网络
文章目录 openGauss学习笔记-219 openGauss性能调优-确定性能调优范围-硬件瓶颈点分析-网络219.1 查看网络状况 openGauss学习笔记-219 openGauss性能调优-确定性能调优范围-硬件瓶颈点分析-网络 获取openGauss节点的CPU、内存、I/O和网络资源使用情况,确认这些资源…...
SAP PP学习笔记- 豆知识01 - 怎么查询既存品目
SAP系统当中已经有哪些品目要怎么查询呢? 1,MM60 品目一览 这里可以输入Plant,然后可以查询该工厂的所有品目。 2,SE16 > MARA MARA 品目一般Data,存放的是品目基本信息。 如果要查询该品目属于哪个Plant&#x…...
相机的机身马达有什么用?
新手疑问: 为什么我的尼康D3200相机明明拥有拍视频能力,但是拍摄视频时却不能对焦 科普时间 那是因为你的相机缺少机身马达,并且你所使用的镜头也没有马达!机身马达是用于给镜头提供对焦动力的装置。它的作用是使相机具备自动对焦功能。如…...
拿捏c语言指针(上)
目录 前言 编辑 指针 内存与地址 计算机常见单位 理解编址 取地址,指针变量,解引用 取地址 指针变量 解引用 指针变量大小 指针类型的作用 char*解引用后 指针-整数 应用 void*指针 const修饰指针变量 const修饰普通变量 const修饰指…...
JVM指令手册
栈和局部变量操作将常量压入栈的指令 aconst_null 将null对象引用压入栈 iconst_m1 将int类型常量-1压入栈 iconst_0 将int类型常量0压入栈 iconst_1 将int类型常量1压入操作数栈 iconst_2 将int类型常量2压入栈 iconst_3 将int类型常量3压入栈 iconst_4 将int类型常量4…...
Linux之多线程
目录 一、进程与线程 1.1 进程的概念 1.2 线程的概念 1.3 线程的优点 1.4 线程的缺点 1.5 线程异常 1.6 线程用途 二、线程控制 2.1 POSIX线程库 2.2 创建一个新的线程 2.3 线程ID及进程地址空间布局 2.4 线程终止 2.5 线程等待 2.6 线程分离 一、进程与线程 在…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果,可同时显示主类&#x…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
