【优选算法】BFS解决FloodFill算法
目录
- FloodFill算法简介
- 一、[图像渲染](https://leetcode.cn/problems/flood-fill/description/)
- 二、[岛屿数量](https://leetcode.cn/problems/number-of-islands/description/)
- 三、[岛屿的最大面积](https://leetcode.cn/problems/max-area-of-island/description/)
- 四、[被围绕的区域](https://leetcode.cn/problems/surrounded-regions/description/)
- 结尾
FloodFill算法简介
Flood Fill 算法(漫水填充算法)是一种经典的图像处理技术,用于将连通区域内的所有像素替换为指定颜色。
核心思想:
从起始点开始,递归或迭代地将与其连通且颜色相同的所有像素替换为目标颜色,直到所有连通像素被处理完毕。
一、图像渲染
题目描述:
思路讲解:
本道题会给我们起始点和目标颜色 ,让我们将二维数组中起始点和与起始点连通并且与起始点颜色相同点的值修改为目标颜色 ,这里使用BFS解决,通过队列逐层处理所有与起始点原始颜色相同的连通像素(上下左右四个方向),并将其替换为目标颜色。以下是具体思路:
- 本道题需要在原二维数组中进行修改,这里先将 [sr, sc] 位置上的值保存为 oldcolor
- 将起始点 [sr, sc] 加入队列,作为 BFS 的起点,并立即将其颜色改为 color
- 重复以下步骤,直到队列为空
- 从队列中取出一个点 [r, c],遍历其四个相邻点 [nr, nc]
- 对每个相邻点,检查是否在二维数组范围内且颜色为 oldcolor
- 若符合条件,将其颜色改为 color,并加入队列,继续处理其相邻点
- 所有连通区域已处理完毕,返回修改后的二维数组
编写代码:
class Solution {// 坐标上下左右需要加的数int dx[4] = {0,0,-1,1};int dy[4] = {1,-1,0,0};
public:vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc , int color) {if (image[sr][sc] == color)return image;int rows = image.size() , cols = image[0].size();queue<pair<int, int>> qu;int oldcolor = image[sr][sc];qu.push({sr, sc});while(!qu.empty()){int qu_x = qu.front().first, qu_y = qu.front().second;image[qu_x][qu_y] = color;qu.pop();for(int i = 0 ; i < 4 ; i++){int x = qu_x + dx[i] , y = qu_y + dy[i] ;if(x >= 0 && x < rows && y >=0 && y < cols && image[x][y] == oldcolor)qu.push({x,y});}}return image;}
};
二、岛屿数量
题目描述:
思路讲解:
本道题给我们一个二维数组,想让我们找出岛屿的数量。
遍历二维数组中的每个位置,当遇到未访问的陆地(‘1’)时,通过 BFS 将其连通的所有陆地标记为 “已访问”(避免重复计数),并计数为一个岛屿,以下是具体思路:
- 初始化岛屿数量为 0,创建一个与原二维数组同等规模的二维数组,并全部初始化为false,表示该位置没有被访问过
- 依次检查网格中的每个位置 [i, j]
- 若当前位置为 ‘1’ 并且未被访问过,则岛屿数量加 1,并使用 BFS 处理整个连通岛屿
- BFS 标记连通陆地:
- 将当前陆地 [i, j] 加入队列,并立即标记为 true(表示已访问,避免重复处理)
- 从队列中取出陆地,遍历其上下左右四个方向的相邻位置
- 对每个相邻位置,若在网格范围内且为 ‘1’ 并且未被访问过,则标记为 true 并加入队列,继续扩展处理
- 遍历完所有位置后,返回岛屿数量
实际上标记岛屿已被访问,可以将 ‘1’ 改为 ‘0’ ,但是我这里为了不修改原数组,就创建一个二维数组,对应原二维数组,标记该位置是否访问过。
编写代码:
class Solution {int dx[4] = {0, 0, -1, 1};int dy[4] = {1, -1, 0, 0};public:int numIslands(vector<vector<char>>& grid) {// 与grid同规模的数组,false代表还没走过,true代表已经走过vector<vector<bool>> vis(grid.size(),vector<bool>(grid[0].size(), false));queue<pair<int, int>> qu;int ans = 0;int rows = grid.size(), cols = grid[0].size();for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {if (grid[i][j] == '1' && vis[i][j] == false) {ans++;vis[i][j] = true;qu.push({i, j});while (!qu.empty()) {int qu_x = qu.front().first, qu_y = qu.front().second;qu.pop();for (int k = 0; k < 4; k++) {int x = qu_x + dx[k], y = qu_y + dy[k];if(x >= 0 && x < rows && y >= 0 && y < cols && grid[x][y] == '1' && vis[x][y] == false){vis[x][y] = true;qu.push({x,y});}}}}}}return ans;}
};
三、岛屿的最大面积
题目描述:
思路讲解:
本道题给我们一个二维数组,想让我们找出最大岛屿。
遍历二维数组中的每个点,当遇到未访问的陆地(‘1’)时,通过 BFS 将其连通的所有陆地标记为 “已访问”(避免重复计数),并在BFS的过程中记录岛屿的大小,通过对比得到最大岛屿的大小,以下是具体思路:
- 初始化最多岛屿大小为 0,创建一个与原二维数组同等规模的二维数组,并全部初始化为false,表示该位置没有被访问过
- 依次检查二维数组中的每个点 [i, j]
- 若当前点为 ‘1’ 并且未被访问过,则岛屿数量加 1,并使用 BFS 处理整个连通岛屿
- BFS 标记连通陆地并记录陆地大小:
- 初始化当前岛屿大小为0
- 将当前陆地 [i, j] 加入队列,并立即标记为 true(表示已访问,避免重复处理)
- 从队列中取出陆地,遍历其上下左右四个方向的相邻点
- 对每个相邻点,若在二维数组范围内且为 ‘1’ 并且未被访问过,则将改点标记为 true ,加入队列并将当前岛屿大小+1,继续扩展处理
- 一次BFS完成后,将当前岛屿大小与最大岛屿大小进行对比,获得最新最大岛屿大小
- 遍历完所有点后,返回岛屿数量
实际上标记岛屿已被访问,可以将 ‘1’ 改为 ‘0’ ,但是我这里为了不修改原数组,就创建一个二维数组,对应原二维数组,标记该点是否访问过。
编写代码:
class Solution {int dx[4] = {0,0,-1,1};int dy[4] = {1,-1,0,0};
public:int maxAreaOfIsland(vector<vector<int>>& grid) {vector<vector<bool>> vis(grid.size(),vector<bool> (grid[0].size(),false));queue<pair<int,int>> qu;int rows = grid.size() , cols = grid[0].size();int ans = 0;for(int i = 0 ; i < rows ; i++){for(int j = 0 ; j < cols ; j++){if(grid[i][j] == 1 && vis[i][j] == false){qu.push({i,j});vis[i][j] = true;int tmp_max = 1;while(!qu.empty()){int qu_x = qu.front().first , qu_y = qu.front().second;qu.pop();for(int k = 0 ; k < 4 ; k++){int x = qu_x + dx[k] , y = qu_y + dy[k];if(x >= 0 && x < rows && y >= 0 && y < cols && grid[x][y] == 1 && vis[x][y] == false){vis[x][y] = true;qu.push({x,y});tmp_max++;}}}if(tmp_max > ans)ans = tmp_max;}}}return ans;}
};
四、被围绕的区域
题目描述:
思路讲解:
本道题给我们一个二维数组,想让我们将被 ‘X’ 包围的 'O’区域中的 ‘O’ 全部替换为 ‘X’,但是想直接找到被 ‘X’ 包围的 ‘O’ 并替换为 'X’需要两个步骤,首先是通过一次BFS判断该区域是否被包围,再通过一次BFS将被 ‘X’ 包围的 ‘O’ 替换为 ‘X’。
正向麻烦的话,就可以反向思考。先找出所有不被围绕的 ‘O’(即位于二维数组边缘或与边缘 ‘O’ 相连的 ‘O’),并将这些 ‘O’ 替换处 ‘O’ 和 ‘X’ 以外的字符;然后将剩余的 ‘O’(被围绕的区域)替换为 ‘X’。以下是具体思路:
- 遍历二维数组中边缘四条边中的每个位置 [i, j]
- 若当前位置为 ‘O’ ,将 ‘O’ 替换为 ‘H’,并使用 BFS 处理整个连通区域
- BFS 处理连通区域:
- 将当前位置 [i, j] 加入队列
- 从队列中取出位置,遍历其上下左右四个方向的相邻位置
- 对每个相邻w位置,若在二维数组范围内且为 ‘O’ ,将 ‘O’ 替换为 ‘H’,继续扩展处理
- 将二维数组中所有的 ‘O’ 替换 ‘X’
- 将二维数组中所有的 ‘H’ 替换 ‘O’
- 处理完毕后,返回二维数组
编写代码:
class Solution {int dx[4] = {0, 0, -1, 1};int dy[4] = {1, -1, 0, 0};int rows, cols;public:void bfs(vector<vector<char>>& board, vector<vector<bool>>& vis, int row , int col) {queue<pair<int, int>> qu;qu.push({row, col});board[row][col] = 'H';while (!qu.empty()) {int qu_x = qu.front().first, qu_y = qu.front().second;qu.pop();for (int k = 0; k < 4; k++) {int x = qu_x + dx[k], y = qu_y + dy[k];if (x >= 0 && x < rows && y >= 0 && y < cols &&board[x][y] == 'O' && vis[x][y] == false) {board[x][y] = 'H';qu.push({x, y});}}}}void solve(vector<vector<char>>& board) {rows = board.size(), cols = board[0].size();vector<vector<bool>> vis(rows, vector<bool>(cols, false));for (int i = 0; i < cols; i++) {if (board[0][i] == 'O' && vis[0][i] == false) {bfs(board, vis, 0 , i);}if (board[rows - 1][i] == 'O' && vis[rows - 1][i] == false) {bfs(board, vis, rows - 1, i);}}for (int i = 0; i < rows; i++) {if (board[i][0] == 'O' && vis[i][0] == false) {bfs(board, vis, i, 0);}if (board[i][cols - 1] == 'O' && vis[i][cols - 1] == false) {bfs(board, vis, i, cols - 1);}}for (int i = 0; i < rows; i++) {for (int j = 0; j < cols; j++) {if (board[i][j] == 'O')board[i][j] = 'X';else if (board[i][j] == 'H')board[i][j] = 'O';}}}
};
结尾
如果有什么建议和疑问,或是有什么错误,大家可以在评论区中提出。
希望大家以后也能和我一起进步!!🌹🌹
如果这篇文章对你有用的话,希望大家给一个三连支持一下!!🌹🌹