当前位置: 首页 > news >正文

算法 --- BFS 解决 FloodFill 算法

BFS 解决 FloodFill 算法

BFS 解决的 Flood Fill 算法题目类型是“连通区域”问题,其适用于需要从某个起点开始,向四周(四或八个方向)扩散,并填充或标记所有相连且具有相同属性的相邻点的题目。


为了更清晰地说明,以下是详细的解释:

1. 核心问题类型:连通区域问题

这类问题的本质是:在一个矩阵(二维网格)中,给你一个“起点”和一个“目标值”。你需要找出所有从起点出发,通过上下左右(可能包括斜对角)移动能够到达的、具有初始相同属性的点,并将这些点修改为新的目标值。

这就像图像处理软件中的“油漆桶”工具,你点击一个点,整个相连的同色区域就会被新的颜色填充。

2. 为什么适用 BFS?

  • 层序遍历:BFS 非常适合模拟“扩散”过程。从起点开始,像水波纹一样一层一层地向外扩展,确保每次填充的都是当前最近、相邻的点。

  • 避免递归栈溢出:对于非常大的网格,DFS 的递归实现可能导致调用栈溢出。而 BFS 使用队列,通常更安全。

  • 最短路径:虽然 Flood Fill 本身不要求最短路径,但其扩散的“层”的概念与计算起点到区域内任意点的最短步数天然契合。

3. 适用题目的关键特征:

当你看到一个题目具有以下一个或多个特征时,就应考虑使用 BFS(或 DFS)进行 Flood Fill:

  1. 数据结构是网格(Grid):题目通常提供一个 m x n 的二维数组(字符或数字),代表地图、图像等。

  2. 操作对象是“区域”:问题要求你计算连通区域的数量标记一个连通区域、或替换一个连通区域的颜色/数值。

  3. 移动方向是相邻的:点的连通性定义为上下左右(四连通)或包括对角线(八连通)。

  4. 起点明确:题目直接指定了开始的坐标 (sr, sc)

4. 经典例题:

  • 图像渲染(Flood Fill):LeetCode 733。

    • 题目:有一幅图像用一个 m x n 的整数网格表示,给你一个起点 (sr, sc) 和一个新颜色 newColor,让你从起点开始对相连的同色区域进行填充(染色)。

    • 为什么适用:完美符合所有特征——网格、连通区域、相邻、起点明确。

  • 岛屿数量:LeetCode 200。

    • 题目:给你一个由 '1'(陆地)和 '0'(水)组成的网格,计算其中岛屿的数量。(岛屿被水包围,由水平或垂直方向的陆地相连形成)

    • 为什么适用:需要遍历网格,每当遇到一块陆地 ’1‘,就以其为起点进行 BFS/DFS,将整个相连的岛屿标记为已访问(相当于“填充”为水‘0’)。这样,BFS 的次数就是岛屿的数量。

  • 被围绕的区域:LeetCode 130。

    • 题目:给你一个 m x n 的矩阵,由字符 'X' 和 'O' 组成。找到所有被 'X' 围绕的区域,并将这些区域里的所有 'O' 用 'X' 填充。

    • 为什么适用:策略是从边界上的 ’O‘ 开始进行 Flood Fill,标记所有不会被围绕的 ’O‘(即与边界连通的)。最后,剩下的未被标记的 ’O‘ 就是被围绕的,需要填充。

  • 腐烂的橘子:LeetCode 994。

    • 题目:网格中每个单元格可能有三个值:0(空)、1(新鲜橘子)、2(腐烂橘子)。每分钟,腐烂橘子会使其相邻的新鲜橘子腐烂。问需要多久才能使所有橘子腐烂。

    • 为什么适用:这是一个多起点的 Flood Fill 问题。所有腐烂的橘子都是起点,同时开始向外(BFS)扩散。整个过程所需的“分钟数”就是 BFS 遍历的层数。

总结

特征描述例子
场景网格、矩阵、地图图像、棋盘、岛屿地图
操作填充、染色、标记、感染、计算连通块油漆桶工具、计算岛屿数、橘子腐烂
移动向四周(四方向/八方向)扩散上下左右
起点单点起点或多点起点一个像素点、多个腐烂源

简单来说:只要问题是在网格上“找一块连在一起的地方并对它做点什么事”,BFS Flood Fill 算法大概率就是解决方案。

题目练习

733. 图像渲染 - 力扣(LeetCode)

算法思路:

可以利用「深搜」或者「宽搜」,遍历到与该点相连的所有「像素相同的点」,然后将其修改成指定的像素即可。

class Solution {
public:int dx[4] = {1, -1, 0, 0};int dy[4] = {0, 0, 1, -1};vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {int prev = image[sr][sc];if(prev == color) return image;queue<pair<int, int>> q;int m = image.size(), n = image[0].size();q.push({sr, sc});while(q.size()){auto [x, y] = q.front();q.pop();image[x][y] = color;for(int k = 0; k < 4; ++k){int a = x + dx[k], b = y + dy[k];if(a >= 0 && a < m && b >= 0 && b < n && image[a][b] == prev){q.push({a, b});}}}return image;}
};

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

算法思路:

遍历整个矩阵,每次找到 一块陆地 的时候:

  • 说明找到 一个岛屿,记录到最终结果 ret 里面;

  • 并且将这个陆地相连的所有陆地,也就是这块 岛屿,全部 变成海洋。这样的话,我们下次遍历到这块岛屿的时候,它 已经是海洋 了,不会影响最终结果。

  • 其中 变成海洋 的操作,可以利用 深搜宽搜 解决,其实就是 733. 图像渲染 这道题~

这样,当我们,遍历完全部的矩阵的时候,ret 存的就是最终结果。

class Solution {
public:int dx[4] = {1, -1, 0, 0};int dy[4] = {0, 0, 1, -1};bool vis[301][301];//false;queue<pair<int, int>> q;int m, n;void bfs(vector<vector<char>>& grid, int i, int j){vis[i][j] = true;q.push({i,j});while(q.size()){auto [x, y] = q.front();q.pop();for(int k = 0; k < 4; ++k){int a = x + dx[k];int b = y + dy[k];if(a >= 0 && a < m && b >= 0 && b < n && grid[a][b] == '1' && !vis[a][b]){q.push({a, b});vis[a][b] = true;}}}}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' && !vis[i][j]){bfs(grid, i, j);ret++;}}}return ret;}
};

695. 岛屿的最大面积 - 力扣(LeetCode)

算法思路:

  • 遍历整个矩阵,每当遇到一块土地的时候,就用 「深搜」 或者 「宽搜」 将与这块土地相连的 「整个岛屿」 的面积计算出来。

  • 然后在搜索得到的 「所有的岛屿面积」 求一个 「最大值」 即可。

  • 在搜索过程中,为了 「防止搜到重复的土地」

    • 可以开一个同等规模的 「布尔数组」,标记一下这个位置是否已经被访问过;

    • 也可以将原始矩阵的 1 修改成 0,但是这样操作会修改原始矩阵。

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

130. 被围绕的区域 - 力扣(LeetCode)

算法思路:

  • 正难则反。

  • 可以先利用 BFS 将与边缘相连的 '0' 区域做上标记,然后重新遍历矩阵,将没有标记过的 '0' 修改成 '1' 即可。

class Solution {
public:int m, n;bool vis[201][201];queue<pair<int, int>> q;int dx[4] = {1, -1, 0, 0};int dy[4] = {0, 0, 1, -1};void solve(vector<vector<char>>& board) {m = board.size(), n = board[0].size();// 初始化vis数组memset(vis, 0, sizeof(vis));// 遍历边界for(int i = 0; i < m; ++i) {if(board[i][0] == 'O' && !vis[i][0]) bfs(board, i, 0);if(board[i][n - 1] == 'O' && !vis[i][n - 1]) bfs(board, i, n - 1);}for(int j = 1; j < n - 1; ++j) {if(board[0][j] == 'O' && !vis[0][j]) bfs(board, 0, j);if(board[m - 1][j] == 'O' && !vis[m - 1][j]) bfs(board, m - 1, j);}// 遍历整个网格,将所有未被标记的 'O' 转换为 'X'for(int i = 0; i < m; ++i) {for(int j = 0; j < n; ++j) {if(board[i][j] == 'O' && !vis[i][j]) board[i][j] = 'X';}}}void bfs(vector<vector<char>>& board, int i, int j) {vis[i][j] = true;q.push({i, j});while(!q.empty()) {auto [x, y] = q.front();q.pop();for(int k = 0; k < 4; ++k) {int a = x + dx[k], b = y + dy[k];if(a >= 0 && a < m && b >= 0 && b < n && board[a][b] == 'O' && !vis[a][b]) {vis[a][b] = true;q.push({a, b});}}}}
};

http://www.dtcms.com/a/390557.html

相关文章:

  • telnet 一个 ip+端口却无法退出 着急
  • UVa1602/LA3224 Lattice Animals
  • Docker BuildKit 实现 Golang 编译加速
  • [x-cmd] 在 Android 的 Termux 和 iOS 的 iSH 中安装 X-CMD
  • CTFSHOW 中期测评(一)web486 - web501
  • android-USB-STM32
  • 云原生周刊:MetalBear 融资、Chaos Mesh 漏洞、Dapr 1.16 与 AI 平台新趋势
  • Android音频学习(十九)——音频HAL层简介
  • Android之音乐列表播放管理类,控制音乐播放、暂停、播放模式的切换等
  • Docker Compose从入门到实战:配置与命令全指南
  • 10.1 输入子系统模型
  • Unity手游输入笔记
  • SpringCloud-注册中心Nacos[笔记3]
  • 关于MySQL与Python后端命令交互备份
  • 大模型上下文工程实践- 上下文管理策略
  • 资产测绘工具-Nmap
  • 智能体环境配置测试
  • 如何将非结构化文档智能解析高质量数据,并按照阅读顺序还原版面?
  • 第八章 惊喜11 认知觉醒
  • 嵌入式系统学习Day37(ARM)
  • 02-Media-10-video_decoder.py 对H.264或H.265格式视频解码并在液晶屏或外接HDMI显示屏上进行显示的示例程序
  • Go面试题及详细答案120题(61-80)
  • 第二部分:VTK核心类详解(第35章:vtkDataSetAttributes数据集属性类)
  • 智能文献分析系统:让AI成为学术研究助手
  • MATLAB基于AHP-熵权法-TOPSIS的学习能力评价研究
  • Ubuntu 部署 PostgreSQL 数据库(附shell脚本一键部署↓)
  • 《数据驱动下的双样本推断:均值与比例的硬核技术实践与方法论思考》
  • Git设置单个仓库用户名和邮箱的方法
  • MongoDB Integer
  • 深度学习第二章 线性代数简介