当前位置:   article > 正文

Offer必备算法24_BFS解决FloodFill_四道力扣题详解(由易到难)

Offer必备算法24_BFS解决FloodFill_四道力扣题详解(由易到难)

目录

BFS解决FloodFill简介

①力扣733. 图像渲染

解析代码

②力扣200. 岛屿数量

解析代码

③力扣695. 岛屿的最大面积

解析代码

④力扣130. 被围绕的区域

解析代码

本篇完。


BFS解决FloodFill简介

        FloodeFill算法即填充算法,中文:洪水灌溉,算法原理就是从一个点开始向四周扩散,向周围可以走到的点填充颜色,直到将可扩散到的点全部填充颜色。

通常使用下面两种方法解决FloodFill算法问题:

  • BFS (宽搜)算法通常使用队列实现,将起始像素点加入队列中,并不断扩展队列中的像素点,直到队列为空为止。
  • DFS(深搜) 算法通常使用递归实现,在处理当前像素点的相邻像素点时,递归调用 DFS 函数,不断深入直到无法找到相邻像素为止。

         假设这一块4*4的方格是一块土地,有凸起的地方,也有凹陷的地方(凹陷的地方用负数表示)。此时下大雨发洪水,会把凹陷的地方填满。绿色圈起来的属于一块区域(上下左右四个方向,有时候题目也会问八个方向包括斜着相连的),题目会问有多少块区域被填满,或者问被填满的最大区域是哪个,或某一块区域的边长是多少。但是本质都是在一块区域找性质相同的连通块。

        DFS:深度优先遍历(递归):从某一点开始一条路走到黑。以最右列的为例,从-1出发,向下->-2->-10->-12,此时发现-12的上下左右都走不了,在拐回去到-10,然后发现-10左边可以走->-4->-3。


        BFS:宽度优先遍历:一层一层拨开。还是以最右边为例,从-1开始拓展一层(上下左右)发现能把-2扩展出来,接着继续扩展一层上下左右,-3和-10扩展出来;接着在扩展一层上下左右,把-4和-12扩展出来。

这时常用两个数组来遍历结点的上下左右结点:

  1.     int dx[4] = {0, 0 , -1, 1}; // i点加dx,dy就是i点的上下左右下标
  2.     int dy[4] = {1, -1 , 0, 0};

①力扣733. 图像渲染

733. 图像渲染

难度 简单

有一幅以 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]]

提示:

  • m == image.length
  • n == image[i].length
  • 1 <= m, n <= 50
  • 0 <= image[i][j], newColor < 216
  • 0 <= sr < m
  • 0 <= sc < n
  1. class Solution {
  2. public:
  3. vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
  4. }
  5. };

解析代码

        可以利用深搜或者宽搜,遍历到与该点相连的所有像素相同的点,然后将其修改成指定的像素即可。后面深搜专题用深搜写,这里用宽搜写:

  1. class Solution {
  2. int dx[4] = {0, 0 , -1, 1}; // i点加dx,dy就是i点的上下左右下标
  3. int dy[4] = {1, -1 , 0, 0};
  4. public:
  5. vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
  6. int prev = image[sr][sc];
  7. if(prev == color)
  8. return image;
  9. int m = image.size(), n = image[0].size(); // 获取大小防止越界
  10. queue<pair<int, int>> q; // 存下标
  11. q.push({sr, sc});
  12. while(!q.empty()) // 模拟层序遍历
  13. {
  14. auto [a, b] = q.front(); // 拿到队头的元素(一个pair)
  15. q.pop();
  16. image[a][b] = color; // 拿到的元素染上新颜色
  17. for(int i = 0; i < 4; ++i)
  18. {
  19. int x = a + dx[i], y = b + dy[i];
  20. if(x >= 0 && x < m && y >= 0 && y < n && image[x][y] == prev)
  21. {
  22. q.push({x, y});
  23. }
  24. }
  25. }
  26. return image;
  27. }
  28. };


②力扣200. 岛屿数量

200. 岛屿数量

难度 中等

给你一个由 '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.length
  • n == grid[i].length
  • 1 <= m, n <= 300
  • grid[i][j] 的值为 '0' 或 '1'
  1. class Solution {
  2. public:
  3. int numIslands(vector<vector<char>>& grid) {
  4. }
  5. };

解析代码

遍历整个矩阵,每次找到一块陆地的时候:说明找到一个岛屿,记录到最终结果 ret 里面。

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

        其中变成海洋的操作,可以利用深搜或宽搜解决,其实就是力扣733. 图像渲染这道题。这样,遍历完全部的矩阵的时候, ret 存的就是最终结果。

(这里用全局bool数组来判断陆地是否被遍历过,也可以直接修改数组,面试的时候可以问面试官是否可以直接修改数组)

  1. class Solution {
  2. int dx[4] = {0, 0 , -1, 1}; // i点加dx,dy就是i点的上下左右下标
  3.     int dy[4] = {1, -1 , 0, 0};
  4. bool vis[301][301] = {false}; // 开题目范围大小
  5. int m = 0, n = 0;
  6. public:
  7. int numIslands(vector<vector<char>>& grid) {
  8. int ret = 0;
  9. m = grid.size(), n = grid[0].size();
  10. for(int i = 0; i < m; ++i)
  11. {
  12. for(int j = 0; j < n; ++j)
  13. {
  14. if(grid[i][j] == '1' && !vis[i][j])
  15. {
  16. ++ret;
  17. bfs(grid, i, j);
  18. }
  19. }
  20. }
  21. return ret;
  22. }
  23. void bfs(vector<vector<char>>& grid, int i, int j)
  24. {
  25. queue<pair<int, int>> q;
  26. q.push({ i, j });
  27. vis[i][j] = true;
  28. while (!q.empty())
  29. {
  30. auto [a, b] = q.front();
  31. q.pop();
  32. for (int i = 0; i < 4; ++i)
  33. {
  34. int x = a + dx[i], y = b + dy[i];
  35. if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == '1' && !vis[x][y])
  36. {
  37. q.push({ x, y });
  38. vis[x][y] = true;
  39. }
  40. }
  41. }
  42. }
  43. };


③力扣695. 岛屿的最大面积

695. 岛屿的最大面积

难度 中等

给你一个大小为 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.length
  • n == grid[i].length
  • 1 <= m, n <= 50
  • grid[i][j] 为 0 或 1
  1. class Solution {
  2. public:
  3. int maxAreaOfIsland(vector<vector<int>>& grid) {
  4. }
  5. };

解析代码

        遍历整个矩阵,每当遇到一块土地的时候,就用深搜或者宽搜将与这块土地相连的整个岛屿的面积计算出来。然后在搜索得到的所有的岛屿面积求一个最大值即可。

        在搜索过程中,为了防止搜到重复的土地,可以开一个同等规模的bool数组,标记一下这个位置是否已经被访问过,也可以将原始矩阵的 1 修改成 0 ,但是这样操作会修改原始矩阵。

  1. class Solution {
  2. int dx[4] = {0, 0 , -1, 1}; // i点加dx,dy就是i点的上下左右下标
  3.     int dy[4] = {1, -1 , 0, 0};
  4. bool vis[51][51] = {false};
  5. int m = 0, n = 0;
  6. public:
  7. int maxAreaOfIsland(vector<vector<int>>& grid) {
  8. m = grid.size(), n = grid[0].size();
  9. int ret = 0;
  10. for(int i = 0; i < m; ++i)
  11. {
  12. for(int j = 0; j < n; ++j)
  13. {
  14. if(grid[i][j] == 1 && !vis[i][j])
  15. {
  16. ret = max(ret, bfs(grid, i, j));
  17. }
  18. }
  19. }
  20. return ret;
  21. }
  22. int bfs(vector<vector<int>>& grid, int i, int j)
  23. {
  24. int ret = 0;
  25. queue<pair<int, int>> q;
  26. q.push({ i, j });
  27. vis[i][j] = true; // 变成true就++ret
  28. ++ret;
  29. while (!q.empty())
  30. {
  31. auto [a, b] = q.front();
  32. q.pop();
  33. for (int i = 0; i < 4; ++i)
  34. {
  35. int x = a + dx[i], y = b + dy[i];
  36. if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 && !vis[x][y])
  37. {
  38. q.push({ x, y });
  39. vis[x][y] = true;
  40. ++ret;
  41. }
  42. }
  43. }
  44. return ret;
  45. }
  46. };


④力扣130. 被围绕的区域

130. 被围绕的区域

难度 中等

给你一个 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.length
  • n == board[i].length
  • 1 <= m, n <= 200
  • board[i][j] 为 'X' 或 'O'
  1. class Solution {
  2. public:
  3. void solve(vector<vector<char>>& board) {
  4. }
  5. };

解析代码

         正难则反。 可以先利用 bfs 将与边缘相连的 'O' 区域修改成无关字符,然后重新遍历矩阵,将没有标记过的 'O' 全修改成 'X' 即可,再把无关字符全还原成'O'。

  1. class Solution {
  2. int dx[4] = {0, 0, -1, 1};
  3. int dy[4] = {1, -1, 0, 0};
  4. int m = 0, n = 0;
  5. public:
  6. void solve(vector<vector<char>>& board) {
  7. m = board.size(), n = board[0].size();
  8. for(int i = 0; i < n; ++i) // 修改边界的O成无关字符
  9. {
  10. if(board[0][i] == 'O')
  11. bfs(board, 0, i);
  12. if(board[m - 1][i] == 'O')
  13. bfs(board, m - 1, i);
  14. }
  15. for(int i = 0; i < m; ++i)
  16. {
  17. if(board[i][0] == 'O')
  18. bfs(board, i, 0);
  19. if(board[i][n - 1] == 'O')
  20. bfs(board, i, n - 1);
  21. }
  22. for(int i = 0; i < m; ++i) // 把剩下的O全修改成X,R全修改成O
  23. {
  24. for(int j = 0; j < n; ++j)
  25. {
  26. if(board[i][j] == 'O')
  27. board[i][j] = 'X';
  28. else if(board[i][j] == 'R')
  29. board[i][j] = 'O';
  30. }
  31. }
  32. }
  33. void bfs(vector<vector<char>>& board, int i, int j)
  34. {
  35. queue<pair<int, int>> q;
  36. q.push({i, j});
  37. board[i][j] = 'R';
  38. while(!q.empty())
  39. {
  40. auto [a, b] = q.front();
  41. q.pop();
  42. for(int i = 0; i < 4; ++i)
  43. {
  44. int x = a + dx[i], y = b + dy[i];
  45. if(x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'O')
  46. {
  47. q.push({x, y});
  48. board[x][y] = 'R';
  49. }
  50. }
  51. }
  52. }
  53. };


本篇完。

下一篇动态规划类型的是01背包问题的OJ。

下下篇是BFS解决最短路类型的OJ。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/436763
推荐阅读
相关标签
  

闽ICP备14008679号