【基础算法总结】BFS 解决 FloodFill 算法

在这里插入图片描述

点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃

FloodFill 算法 就是在一个矩阵中找性质相同的连通块。关于这个在《递归、搜索回溯专题》 有详细介绍。并且用的是DFS解决问题,这里主要是用BFS解决FloodFill 算法。

1.图像渲染

题目链接:733. 图像渲染

题目分析:

在这里插入图片描述

这道题的意思是,给一个开始位置,把和它性质相同的连通块都找到,并且渲染成color。注意可以上下左右四个方向去找。

算法原理:

解法:BFS

BFS(宽度优先遍历/宽度优先搜索/层序遍历),借助队列实现,一层一层出。
起始位置先入队。队列不空出队,把它上下左右和它颜色相同的放入队列。然后循环下去,直到队列为空。

在这里插入图片描述

class Solution 
{
    //处理数对,经常用到pair,可以typedef一下
    typedef pair<int,int> PII;
    //向量数组
    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 prev = image[sr][sc];//先标记一下需要修改的像素值
        int m = image.size(), n = image[0].size();

        queue<PII> q;
        q.push({sr,sc});
        while(q.size())
        {
        	//first -> a, second -> b
            auto [a,b] = q.front();
            q.pop();
            image[a][b] = color;

            //上下左右四个方向
            for(int i = 0; i < 4; ++i)
            {
                int x = a + dx[i], y = b + dy[i];
                if(x >= 0 && x < m && y >= 0 && y < n && image[x][y] == prev)
                {
                    q.push({x,y});
                }
            }
        }
        return image;
    }
};

2.岛屿数量

题目链接:200. 岛屿数量

题目描述:

在这里插入图片描述

求岛屿的数量,每座岛屿只能上下左右去找。

在这里插入图片描述

算法原理:

解法:BFS

从左往右扫描这个矩阵,当遇到陆地的时候就把和陆地相连的岛屿找到,如何找,就是BFS从这个地方宽搜一遍,就可以把连通的区域找到。可以搞一个变量ret 记录陆地数量就可以。但是这里层序遍历有一个问题。从一个位置扩展到下一个位置,不能在从下一个位置在回到上一个位置不然就死循环了。为了避免这种情况我们有两种方式。

  1. 修改原始矩阵的值
  2. 搞一个同等矩阵大小的bool类型的二维数组,记录当前位置是否已经被搜索过,false表示没有,true表示搜索过。并且当前位置bfs一次之后。和它相连的其他岛屿也被置为true,然后从左往右遍历的时候即使它是1,也不会进去了。
class Solution 
{
    //bool vis[301][301];  //感觉浪费空间就先计算一下当前需要多大空间,然后再申请
    vector<vector<int>> vis;
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
     int m,n;
public:


    int numIslands(vector<vector<char>>& grid) 
    {
        m = grid.size(), n = grid[0].size();
        vis.resize(m,vector<int>(n));
        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++;
                    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], y = b + dy[k];
                if(x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] == '1')
                {                  
                    q.push({x,y});
                    vis[x][y] = true;
                }
            }

        }
    }
};

3.岛屿的最大面积

题目链接:695. 岛屿的最大面积

题目分析:

在这里插入图片描述
这道题是求那一个连通块的面积是最大的。

算法原理:

解法:BFS

还是从左往右扫完,当碰到一块土地就去找和这个土地连通的所有土地,如何找?还是用BFS遍历一次,同时要有一个变量count统计这个岛屿面积有多大。注意找过的土地下一次不能在找了!因此我们还是来一个bool类型的二维数组来标记那些土地是已经被搜索过了!搜索过了下一次就不能找了。

class Solution 
{
    bool vis[51][51];
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
    int m, n;
public:

    int maxAreaOfIsland(vector<vector<int>>& 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])
                {                
                    ret = max(ret,bfs(grid, i, j));
                }
            }
        }
        return ret;

    }

    int bfs(vector<vector<int>>& gird, int i, int j)
    {
        int count = 0;
        queue<pair<int,int>> q;
        q.push({i,j});
        vis[i][j] = true;
        ++count;
        
        while(q.size())
        {
            auto [a,b] = q.front();
            q.pop();

            for(int k = 0; k < 4; ++k)
            {
                int x = a + dx[k], y = b + dy[k];
                if(x >= 0 && x < m && y >= 0 && y < n && gird[x][y] == 1 && !vis[x][y])
                {
                    q.push({x,y});
                    vis[x][y] = true;
                    ++count;
                }
            }
        }
        return count;
    }
};

4.被围绕的区域

题目链接: 130. 被围绕的区域

题目分析:

在这里插入图片描述

把被 ’ X ’ 围绕的 ’ O '区域修改为 ’ X ',被围绕的区域指的是这个区域的 ’ O ’ 没有处于边界。

算法原理:

如下面这张图只有一处区域是要被修改的,此时我们是有两种解法的。

解法一:直接做

从左往右扫描,当遇到 ‘O’ 之后,就把与这个 ‘O’ 相连的 ‘O’ 区域都修改成 ‘X’,这是我们之前做的方式。但是这里有一个问题当我在扫描遇到 ‘O’ 也会把下面绿色区域修改成 ‘X’ ,但是这块区域有 ‘O’ 是处于边界的,因此这一块区域是不能够被修改的!所以在BFS过程中遇到 ‘O’ 处于边界的情况这块是不能被修改的。
在这里插入图片描述
更为极端的例子是,当BFS是把这个区域遍历一遍之后才知道这个区域是不能够被修改的,还要把已经被修改过成 ‘X’ 还原成 ‘O’。但是我们BFS是一边修改一边遍历的。然后遍历到 ‘O’ 处于边界才知道这个区域是不能被修改的,并且刚才修改过的是错的,还要还原回去,但此时前面变成 ‘X’ 的 ‘O’ 特别难被还原回去。

在这里插入图片描述
不过还可以这样,先去BFS遍历一遍看看这个区域是否合法,如果合法在去BFS把这块区域修改成 ‘X’,需要两个BFS。

解法二:正难则反
正着来不太好处理与边界相连的连通块,那就反着来就先处理与边相连的连通块。先遍历四条边界,在四条边界发现有 ‘O’ ,仅对这些边界上的 ‘O’ 来一次BFS,把这个连通块都修改成 ‘.’。这样把处于边界的 ‘O’ 连通快都修改成 '.'之后,接下来仅需遍历一下矩阵,把矩阵中的剩下的 ‘O’ 修改成 ‘X’ ,把 ‘.’ 还原成 'O’就可以了。

  1. 先处理边界上 ‘O’ 区域
  2. 扫描矩阵,还原即可
class Solution {
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    int m,n;
public:

    void solve(vector<vector<char>>& board) {
        m=board.size(),n=board[0].size();

        // 1.把边界的 O 相连的连通块,全部修改成 .
        for(int i = 0; i < m; ++i)
            for (int j = 0; j < n; ++j)
                if(board[i][j] == 'O')
                    if(i == 0 || i == m-1 || j == 0 || j == n-1) 
                        bfs(board,i,j);
        // 2.还原
        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';
            }

    }

    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], y = b + dy[k];
                if(x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'O')
                {
                    q.push({x,y});
                    board[x][y] = '.';
                }
            }
        }
    }
};
  • 45
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 57
    评论
评论 57
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值