【C++】BFS解决Floodfill问题

目录

Floodfill算法介绍

解决方法

BFS

图画渲染

算法思路:

代码实现:

岛屿数量

算法思路

代码实现

岛屿的最大面积 

算法思想

代码实现

被围绕的区域

算法思路

代码实现

总结:


Floodfill算法介绍

Floodfill翻译过来就是“洪水灌溉”;什么事洪水灌溉呢?先来看一幅图;

图中的数字代表的是陆地的高度,水往地处流,这里我们用负数代表低洼的区域,所以这个图中可以分为陆地和水域两种区域,水域是被陆地包裹着的;

洪水来了之后,绿色的部分就会被填充;

那么洪水来了之后,我们应该如何操作才能将低洼的地区填满(处理)呢?

解决方法

我们可以使用DFS和BFS来解决此类问题,本章博客我分享下BFS的解决方法;

BFS

BFS即Breadth First Search,即广度优先搜索。如果说DFS是一条路走到黑的话,BFS就完全相反了。BFS会在每个岔路口都各向前走一步。以二叉树为例;

与二叉树的层序遍历无出其右,一层一层的进行处理数据,每次只走一步;实现BFS的关键就是使用队列;

 BFS的代码实现其实也是有模版的,接下来我们来解决几道例题,心中自然就会明了了;

图画渲染

例题地址:. - 力扣(LeetCode)

算法思路:

这道题的思路很简单,题给出了一个初始的坐标,然后叫我们把这块区域周围与此坐标的值相等的连通区域改成目标颜色;
这里把image[sr][sv]赋值给pre;color是要更改的最终的颜色;
思路:如果给的pre处的颜色与要更改的颜色相同,那么就不需要操作;因为我们要更改的就是pre周围与他相同颜色的区域;如果pre处的颜色不是color,那么我们就需要通过BFS来把周围颜色与pre处相同的区域更改成color;
2.在floodfill函数中我们只需要调用一次BFS即可;
3.实现BFS算法:
a.传参我们需要把数组和坐标传过去,这道题还需要传递color;
b.m,n分别为数组中的行和列,用与边界的判断;
遍历技巧:定义两个数组dx和dy;这两个数组是用来就是遍历的,就下面的数组来看坐标x=a+dx[i],y=b+dy[i];进行四次遍历,dx,dy分别是(0,1)、(0,-1)、(1,0)、(-1,0)就是分别访问了以(a,b)为中心的上下左右四个方向;
visit数组是用来进行标记访问过区域的;因为有的情况前面访问过的区域还可能会被再次访问,为了避免这种情况我们需要进行标记;
c.因为我们之后要把pre处颜色更改,遍历需要与之前的颜色进行对比,所以我们使用tmp记下来pre处的颜色;如果pre处的颜色是color直接返回即可;
d.构建个队列q,然后把pre压进去,再把pre的颜色更改为color,在标记下访问过了;
e.使用while循环,接下来就是层序遍历的代码模版了,这道题的核心就是遍历然后标记更改颜色;

代码实现:

class Solution {
public:

    int dx[4]={0,0,1,-1};
    int dy[4]={ 1,-1,0,0};
    bool visit[60][60]; 
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
        bfs(image,sr,sc,color);
        return image; 
    }
    void  bfs(vector<vector<int>>& image, int sr, int sc, int color)
    {
        int m=image.size();int n=image[0].size();
        int tmp=image[sr][sc];
        if(image[sr][sc]!=color)
        {
            image[sr][sc]=color;
            visit[sr][sc]=true; 
        }
        queue<pair<int,int>>q;
        q.push({sr,sc});
        while(q.size())
        {
            auto [a,b]=q.front();
            q.pop();
            for(int i=0;i<4;i++)
            {
                int x=a+dx[i];int y=b+dy[i];//上下左右进行遍历
                if(x>=0&&x<m&&y>=0&&y<n&&!visit[x][y]&&image[x][y]==tmp)//边界判断
                {
                    q.push({x,y});//能进来的都是合法的坐标
                    image[x][y]=color;//更改颜色
                    visit[x][y]=true;//标记访问过了
                }
            }
        }
    }

};

岛屿数量

例题地址:. - 力扣(LeetCode)

算法思路

这道题与上一道题类似,我们只要找到‘1’,然后使用BFS将这联通块+处理一下(避免重复),每次找到一块岛屿就count++;最后返回即可;有的答案修改了原数组的值,这是一个接口函数,如果是在项目中最好不要修改原数组;保证函数的功能正确运行。

vis数组标记访问过的合法区域;dx、dy用来遍历

1.在numIslands函数中对二维数组进行扫描,如果遇到了一个‘1’,count++;然后就调用BFS把这块联通的区域标记一下,也就是把整个岛屿标记了、已经统计过了;m,n是数组的行和列;
2.老一套了,使用层序遍历,创建队列,压进去,标记,遍历,把合法的都标记下,塞进队列里;

代码实现

class Solution {
public:
 int dx[4] = {1, -1, 0, 0};
 int dy[4] = {0, 0, 1, -1};
    bool  vis[301][301];
    int m,n;
    int numIslands(vector<vector<char>>& 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'&&vis[i][j]==false)
                {
                    ret++;
                    bfs(grid,i,j);
                }
            }
        }
        return ret;
    }

    void bfs(vector<vector<char>>& grid,int i,int j)
    {
        queue<pair<int,int>>q;
        q.push({i,j});
        vis[i][j]=true;
        while(q.size())
        {
            auto [a,b]=q.front();

            q.pop();
            for(int k=0;k<4;k++)
            {
                int x=a+dx[k];int y=b+dy[k];
                if(x>=0&&x<m&&y>=0&&y<n&&grid[x][y]=='1'&&!vis[x][y])
                {
                    q.push({x,y});
                    vis[x][y]=true;//标记岛屿土地
                }
            }    
        }       
    }
};

岛屿的最大面积 

例题地址:. - 力扣(LeetCode)

算法思想

这道题找出最大的岛屿面积,在maxAreaOfIsland函数中,我们只需要扫描数组,找到‘1’,就调用BFS,并进行比较取较大的;
BFS实现:老套路,将这一联通块标记,在标记的同时要计算面积;每次向队列中插入一个数据就count++;最后返回面积;

代码实现

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

被围绕的区域

例题地址:. - 力扣(LeetCode)

算法思路

这道题我们正着做比较难,但是我们可以反着做,先把边界的O的联通块通过BFS更改成一个无关字符,这里我更改为点,然后我们在进行原数组中的扫描,如果遇到‘O’,那就修改为‘X’,遇到‘点’就改成‘O’;

BFS实现:m,n数组的行和列,dx,dy用来遍历,开一个队列,一层一层的把合法的插入进去,同时更改成‘点’;

代码实现

class Solution {
public:
    int m,n;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    void solve(vector<vector<char>>& board) {
        m=board.size();n=board[0].size();
        //现将边缘的进行处理
        for(int i=0;i<m;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<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=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(board[i][j]=='O')
                {
                    board[i][j]='X';
                }
                else if(board[i][j]=='.')
                {
                    board[i][j]='O';
                }
            }
        }
        return ;
    }
    
    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())
        {
            auto [a,b]=q.front();
            q.pop();
            for(int k=0;k<4;k++)
            {
                int x=a+dx[k];int y=b+dy[k];
                if(x>=0&&x<m&&y>=0&&y<n&&board[x][y]=='O')
                {
                    q.push({x,y});
                    board[x][y]='.';
                }
            }
        }
        return ;
    }
};

总结:

BFS框架是层序遍历(队列实现),核心在于遍历内部的代码,根据题目进行处理,另外就是遍历的技巧十分的方便。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值