C++算法(15)BFS_FloodFill

FloodFill问题的实质是找出性质相同的连通块

1.图像渲染

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

题目的要求是在题给二维数组中找到和初始位置性质相同的连通块并修改为题目要求的性质,那么对于每一个在上下左右区域找到的方格,都还要遍历其的上下左右区域,直至全部都不符合要求为止,其实相当于层序遍历了,可以用之前队列+宽搜的方法写出大逻辑,此时要注意几点细节:

①队列中储存pair<int,int>,键值对是二维数组中的坐标

②由于要遍历上下左右四个区域,可能第一时间想要用四个if来写,但还有一种更简洁的写法,定义两个数组:

,对于每一个pair,将键和值加上这四对数(用循环),即是对上下左右四个区域都做了检查,看是否有符合条件的入队

③之前由于做队列-宽搜问题时都要求的是以一组一组的形式返回答案,(一组一组代表二叉树的一层一层),所以要先计算队列中该层元素的个数,再循环该层元素的个数次,再这层循环里处理问题,但这道题只需要将每层都处理了即可,所以不用加一层循环sz

④对于什么时候修改方块为题目要求的性质,可能比较容易想到在每判断到一个方块为同性质时再修改,但这意味着初始位置要先改性质一次,再在判断后改性质,分成了两块,更清晰的写法是要认识到队列会遍历每一个同性质的方块,所以直接在队列遍历时更改即可,每一个在队列里的pair都会被修改。

⑤最后还有一个不易察觉的问题:若初始位置的性质已经和题目要求的性质一样了,那么周围若有和该初始方块性质相同的连通块,那就已经符合题目条件了,若有和该初始方块性质不同的连通块,那也不用修改,所以直接返回原始二维数组即可,如果继续执行接下来的逻辑,已出队的pair会反复被加入队列,形成死循环。

上图表示和二叉树类似的层序遍历过程

代码如下:

cpp 复制代码
class Solution {
public:
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) 
    {
        int dx[]={1,-1,0,0};
        int dy[]={0,0,1,-1};
        int pre=image[sr][sc];
        if(pre==color)
        return image;
        queue<pair<int,int>> tmp;
        tmp.push({sr,sc});
        int m=image.size();
        int n=image[0].size();
        while(tmp.size())
        {
            image[tmp.front().first][tmp.front().second]=color;
            for(int i=0;i<4;i++)
            {
                int cur1=tmp.front().first+dx[i];
                int cur2=tmp.front().second+dy[i];
                if(cur1<m&&cur1>=0&&cur2<n&&cur2>=0&&image[cur1][cur2]==pre)
                {
                    tmp.push({cur1,cur2});
                }
            }
            tmp.pop();                               
        }
        return image;
    }
};

2.岛屿数量

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

上一道题是从题给初始位置开始查找,相当于只找一个岛屿,现在是找二维数组中的所有岛屿,故要对每一个方格进行遍历,然后进行BFS的逻辑,具体的不再赘述,但仍然按照上一道题的写法是会超出时间限制的,问题在于:修改性质的方法,在上一题中以为是在分析队列的每一个节点时修改会好一点,因为这样逻辑比较清晰,然而这道题涉及的遍历,修改性质要改得越早越好,不然遍历同层的节点时会把已经加入队列的节点重复加入,多了一些冗余的步骤,仔细想想,仍然觉得是根节点修改性质,找到新节点后也即刻修改比较好,效率应该是最高的。另外还有一点要注意:为了保护原始数据,还是尽量选择不修改原始数据的写法:创建一个bool数组记录该位置是否被遍历过(替换上面的修改性质),还要注意bool数组要放到全局,或者自行在局部初始化为false,否则里面放的全是随机值

代码如下:

cpp 复制代码
#include <queue>
#include <vector>
class Solution {
    bool vis[301][301];
public:
    int numIslands(vector<vector<char>>& grid) 
    {
        int m=grid.size();
        int n=grid[0].size();
        vector<int> dx={1,-1,0,0};
        vector<int> dy={0,0,1,-1};
        int count=0;
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(grid[i][j]=='1'&&!vis[i][j])
                {
                    queue<pair<int,int>> q;
                    q.push({i,j});
                    vis[i][j]=true;
                    while(q.size())
                    {
                        auto[a,b]=q.front();
                        for(int x=0;x<4;x++)
                        {
                            int cur1=a+dx[x];
                            int cur2=b+dy[x];
                            if(cur1>=0&&cur1<m&&cur2>=0&&cur2<n&&grid[cur1][cur2]=='1'&&!vis[cur1][cur2])
                            {
                                q.push({cur1,cur2});
                                vis[cur1][cur2]=true;
                            }
                        }
                        q.pop();
                    }
                    count++;
                }
            }
        }
        return count;
    }
};

3.岛屿的最大面积

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

和上一题的底层逻辑一样,只需要注意用一个变量去维护岛屿的面积即可。

代码如下:

cpp 复制代码
#include<queue>
#include<algorithm>
class Solution {
public:
    bool vis[51][51];
    int maxAreaOfIsland(vector<vector<int>>& grid) 
    {
        int dx[4]={1,-1,0,0};
        int dy[4]={0,0,1,-1};
        queue<pair<int,int>> q;
        int m=grid.size();
        int 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])
                {
                    int count=0;
                    q.push({i,j});
                    vis[i][j]=true;
                    while(q.size())
                    {
                        count++;
                        int a=q.front().first;
                        int b=q.front().second;
                        for(int k=0;k<4;k++)
                        {
                            int cur1=a+dx[k];
                            int cur2=b+dy[k];
                            if(cur1<m&&cur1>=0&&cur2<n&&cur2>=0&&grid[cur1][cur2]==1&&!vis[cur1][cur2])
                            {
                                q.push({cur1,cur2});
                                vis[cur1][cur2]=true;
                            }
                        }
                        q.pop();
                    }
                    ret=max(count,ret);
                }
            }
        }
       return ret; 
    }
};

4.被围绕的区域

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

由于二维数组外围的'O'形成的区域块是不被包围的,所以按理说不用修改,但除去这些区域外其他的'O'要被改成'X',为了和这些'O'区别开,好一次性处理,所以找出二维数组外围的'O'形成的区域块后先改成任意其他字符,最后遍历全数组,遇见'O'则改成'X',遇见其他字符则改成'O'

代码如下:

cpp 复制代码
class Solution {
public:
    int dx[4]={1,-1,0,0};
    int dy[4]={0,0,1,-1};
    int m,n;
    void solve(vector<vector<char>>& board) 
    {
        m=board.size();
        n=board[0].size();
        for(int j=0;j<n;j++)
        {
            if(board[0][j]=='O')
            {
                bfs(board,0,j);
            }
            if(board[m-1][j]=='O')
            {
                bfs(board,m-1,j);
            }
        }
        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);
            }
        }
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(board[i][j]=='O')
                {
                    board[i][j]='X';
                }
                if(board[i][j]=='.')
                {
                    board[i][j]='O';
                }
            }
        }   
    }
    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())
        {
            int a=q.front().first;
            int b=q.front().second;
            for(int k=0;k<4;k++)
            {
                int cur1=a+dx[k];
                int cur2=b+dy[k];
                if(cur1>=0&&cur1<m&&cur2>=0&&cur2<n&&board[cur1][cur2]=='O')
                {
                    q.push({cur1,cur2});
                    board[cur1][cur2]='.';
                }
            }
            q.pop();
        }
    }
};
相关推荐
小王C语言2 小时前
【基础IO】————简单设计一下libc库
前端·数据结构·算法
亦复何言??2 小时前
BeyondMimic 论文解析
人工智能·算法·机器人
WolfGang0073212 小时前
代码随想录算法训练营 Day20 | 回溯算法 part02
算法
YXXY3132 小时前
前缀和算法
算法
客卿1232 小时前
滑动窗口--模板
java·算法
_日拱一卒2 小时前
LeetCode:滑动窗口的最大值
数据结构·算法·leetcode
codeの诱惑3 小时前
推荐算法(一):数学基础回顾——勾股定理与欧氏距离
算法·机器学习·推荐算法
落樱弥城3 小时前
Vulkan Compute 详解
算法·ai·图形学
Book思议-3 小时前
【数据结构】字符串模式匹配:暴力算法与 KMP 算法实现与解析
数据结构·算法·kmp算法·bf算法