FloodFill

"绝境之中才窥见,Winner,Winner" 


FloodFill算法简介:

        floodfill又翻译成漫水填充。我们可以将下面的矩阵理解为一片具有一定高度的坡地,此时突发洪水,洪水会将高度<0的地方填满。        

        话句话来说,FloodFill算法是为了找出那些具有相同性质的区域。那我们现在就来看看一些经典的使用floodfill算法的题目。


图像渲染

(1) 题目解析        

                这类题目的代码部分,都是将模拟的过程转化成代码。

(2) 算法代码

dfs:

class Solution {
public:
    int m,n;
    int prev;
    int dx[4] = {0,0,1,-1};
    int dy[4] = {1,-1,0,0};
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
        m = image.size(),n = image[0].size();
        if(image[sr][sc] == color) return image;
        prev = image[sr][sc];
        dfs(image,sr,sc,color);
        return image;
    }

    void dfs(vector<vector<int>>& image, int i,int j,int newcolor)
    {
        // 从这一点出发,每一个方向去 递归相邻节点
        image[i][j] = newcolor;
        for(int k=0;k<4;++k)
        {
            int x=i+dx[k],y = j+dy[k];
            // 满足 要渲染更改的值 
            if(x>=0 && x<m && y>=0 && y<n && image[x][y] == prev)
            {
                dfs(image,x,y,newcolor);
            }
        }
    }
};

 bfs:       

class Solution {
public:
    typedef pair<int,int> PII;
    int m,n;
    int prev;
    int dx[4] = {0,0,1,-1};
    int dy[4] = {1,-1,0,0};
    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
        m = image.size(),n = image[0].size();
        if(image[sr][sc] == color) return image;
        // 当前位置会被修改为newcolor 这里是保存需要被渲染的值
        prev = image[sr][sc];
        bfs(image,sr,sc,color);
        return image;
    }

    void bfs(vector<vector<int>>& image, int i,int j,int newcolor)
    {
        queue<PII> que;
        image[i][j] = newcolor;
        que.push({i,j});
        while(que.size())
        {
            // 取出已经被渲染的位置,并将它的相邻位置,满足条件的 插入进队列
            auto [a,b] = que.front();
            que.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 && image[x][y] == prev)
                {
                    image[x][y] = newcolor;
                    que.push({x,y});
                }
            }
        }
    }
};

岛屿数量

(1) 题目解析        

注: 进行bfs或dfs时需要注意,防止统计已经遍历过的位置。我们有两种解决方法,其一就是对原数组内部的值进行修改,区别于目标条件,其二就是创建一个新的布尔数组,标记每一个遍历过的点。

(2) 算法代码

dfs:        

class Solution {
public:
    int m,n;
    bool vis[301][301];
    int dx[4] = {0,0,1,-1};
    int dy[4] = {1,-1,0,0};
    int numIslands(vector<vector<char>>& grid) {
        m = grid.size(),n = grid[0].size();
        int count = 0;
        for(int i=0;i<m;++i)
            for(int j=0;j<n;++j)
            {
                // 遍历到岛屿进行一次dfs 并且这个位置没有被访问过
                if(grid[i][j] == '1' && !vis[i][j]) 
                {
                    count++;
                    vis[i][j] = true; // 标记
                    dfs(grid,i,j);
                }
            }
        
        return count;
    }

    void dfs(vector<vector<char>>& grid,int i,int j)
    {
        // 这里的dfs 只需将与(i,j) 相连的岛屿统计即可
        for(int k=0;k<4;++k)
        {
            int x=i+dx[k],y=j+dy[k];
            if(x>=0 && x<m && y>=0 && y<n && grid[x][y] == '1' && !vis[x][y])
            {
                vis[x][y] = true;
                dfs(grid,x,y);
            }
        }
    }
};

bfs:

    class Solution {
    public:
        int m,n;
        bool vis[301][301];
        int dx[4] = {0,0,1,-1};
        int dy[4] = {1,-1,0,0};
        int numIslands(vector<vector<char>>& grid) {
            m = grid.size(),n = grid[0].size();
            int count = 0;
            for(int i=0;i<m;++i)
                for(int j=0;j<n;++j)
                {
                    // 遍历到岛屿进行一次dfs 并且这个位置没有被访问过
                    if(grid[i][j] == '1' && !vis[i][j]) 
                    {
                        count++;
                        vis[i][j] = true; // 标记
                        bfs(grid,i,j);
                    }
                }
            
            return count;
        }

        void bfs(vector<vector<char>>& grid,int i,int j)
        {
            // 这里的bfs 只需将与(i,j) 相连的岛屿统计即可
            // bfs通常需要借助队列来实现
            queue<pair<int,int>> que;
            que.push({i,j});
            while(que.size())
            {
                auto [a,b] = que.front();
                que.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 && grid[x][y] == '1' && !vis[x][y])
                    {
                        vis[x][y] = true;
                        que.push({x,y});
                    }
                }
            }
        }
    };

岛屿的最大面积

(1) 题目解析        

        这道题和上个题目十分类似,只不过这道题目的结果是求这些岛屿的最大面积,所以,我们需要对相连岛屿数量进行统计,并最终返回一个最大值。

(2) 算法代码

dfs:

class Solution {
public:
    int m,n;
    bool vis[301][301];
    int dx[4] = {0,0,1,-1};
    int dy[4] = {1,-1,0,0};
    int ret,count;
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        m = grid.size(),n = grid[0].size(),ret = 0;
        for(int i=0;i<m;++i)
            for(int j=0;j<n;++j)
            {
                // 遍历到岛屿进行一次dfs 并且这个位置没有被访问过
                if(grid[i][j] == 1 && !vis[i][j]) 
                {
                    count = 0;
                    dfs(grid,i,j);
                    ret = max(ret,count);
                }
            }
        return ret;
    }

    void dfs(vector<vector<int>>& grid,int i,int j)
    {
        vis[i][j] = true;
        count++;
        for(int k=0;k<4;++k)
        {
            int x=i+dx[k],y=j+dy[k];
            if(x>=0 && x<m && y>=0 && y<n && grid[x][y] == 1 && !vis[x][y])
            { 
                dfs(grid,x,y);
            }
        }
    }
};

bfs:

class Solution {
public:
    int m,n;
    bool vis[301][301];
    int dx[4] = {0,0,1,-1};
    int dy[4] = {1,-1,0,0};
    int ret,count;
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        m = grid.size(),n = grid[0].size(),ret = 0;
        for(int i=0;i<m;++i)
            for(int j=0;j<n;++j)
            {
                // 遍历到岛屿进行一次dfs 并且这个位置没有被访问过
                if(grid[i][j] == 1 && !vis[i][j]) 
                {
                    count = 0;
                    vis[i][j] = true; // 标记
                    bfs(grid,i,j);
                    ret = max(ret,count);
                }
            }
        return ret;
    }

    void bfs(vector<vector<int>>& grid,int i,int j)
    {
        // 这里的bfs 只需将与(i,j) 相连的岛屿统计即可
        // bfs通常需要借助队列来实现
        queue<pair<int,int>> que;
        que.push({i,j});
        count++;
        while(que.size())
        {
            auto [a,b] = que.front();
            que.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 && grid[x][y] == 1 && !vis[x][y])
                {
                    count++;
                    vis[x][y] = true;
                    que.push({x,y});
                }
            }
        }
    }
};


被围绕的区域

(1) 题目解析        

        但其实,我们发现这两个dfs本质是做同样的事情,但却因为判断边界,我们需要写两份不同的dfs,这是蛮麻烦的事情。我们换个思路,直接用一个dfs将边界的'O',进行过滤,并填上不相关的字符,再将整个数组遍历,遇到‘O’改为‘X’,遇到这个不相关的字符,改为'O'。

(2) 算法代码

dfs:

class Solution {
public:
    int m,n;
    bool vis[201][201];
    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 j=0;j<n;++j)
        {
            if(board[0][j] == 'O') dfs(board,0,j);
            if(board[m-1][j] == 'O') dfs(board,m-1,j);
        }

        // 第一列 最后一列
        for(int i=0;i<m;++i)
        {
            if(board[i][0] == 'O') dfs(board,i,0);
            if(board[i][n-1] == 'O') dfs(board,i,n-1);
        }

        for(int i=0;i<m;++i)
            for(int j=0;j<n;++j)
            {
                if(board[i][j] == '.') board[i][j] = 'O';
                else if(board[i][j] == 'O') board[i][j] = 'X';
            }
    }

    void dfs(vector<vector<char>>& board,int i,int j)
    {
        board[i][j] = '.';
        for(int k=0;k<4;++k)
        {
            int x=i+dx[k],y=j+dy[k];
            if(x>=0 && x<m && y>=0 && y<n && board[x][y] == 'O')
            {
                dfs(board,x,y);
            }
        }
    }
};

bfs:        

class Solution {
public:
    int m,n;
    bool vis[201][201];
    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 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=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<m;++i)
            for(int j=0;j<n;++j)
            {
                if(board[i][j] == '.') board[i][j] = 'O';
                else if(board[i][j] == 'O') board[i][j] = 'X';
            }
    }

    void bfs(vector<vector<char>>& board,int i,int j)
    {
        board[i][j] = '.';
        queue<pair<int,int>> que;
        que.push({i,j});
        while(que.size())
        {
            auto [a,b] = que.front();
            que.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')
                {
                    board[x][y] = '.';
                    que.push({x,y});
                }
            }
        }
    }
};

扫雷

(1) 题目解析        

        经典的扫雷游戏,至于玩法也就不过多解释。该题目考察的地方就在于,点击一个点之后,从这个 点开始进行对相邻领域的展开,我们可以采用dfs或bfs完成。

        当挖出的位置是一个雷,将这个位置标记成‘X’,游戏结束。当挖出的一个地方是空位置,你需要首先去相邻区域查找是否有雷,如果没有发现雷,则标记为'B',否则就需要将搜查到的雷的个数,标记在该位置处。

(2) 算法代码

dfs:

class Solution {
public:
    int m,n;
    bool vis[51][51];
    // 扫雷位置有八个位置需要被展开
    int dx[8] = {0,0,-1,1,-1,1,-1,1};
    int dy[8] = {1,-1,0,0,1,-1,-1,1};
    vector<vector<char>> updateBoard(vector<vector<char>>& board, vector<int>& click) {
        m = board.size(),n = board[0].size();
        int i = click[0],j = click[1];
        if(board[i][j] == 'M')
            board[i][j] = 'X';
        else
            dfs(board,i,j);
        return board;
    }

    void dfs(vector<vector<char>>& board,int i,int j)
    {
        int count = 0;
        // 统计雷的数量
        for(int k=0;k<8;++k)
        {
            int x=i+dx[k],y=j+dy[k];
            if(x>=0 && x<m && y>=0 && y<n && board[x][y] == 'M') count++;
        }

        if(count){
            board[i][j] = count + '0';
        }
        else{
            board[i][j] = 'B';
            for(int k=0;k<8;++k)
            {
                int x=i+dx[k],y=j+dy[k];
                if(x>=0 && x<m && y>=0 && y<n && board[x][y] == 'E') dfs(board,x,y);
            }
        }
    }
};

bfs:        

class Solution {
public:
    int m,n;
    bool vis[51][51];
    // 扫雷位置有八个位置需要被展开
    int dx[8] = {0,0,-1,1,-1,1,-1,1};
    int dy[8] = {1,-1,0,0,1,-1,-1,1};
    vector<vector<char>> updateBoard(vector<vector<char>>& board, vector<int>& click) {
        m = board.size(),n = board[0].size();
        int i = click[0],j = click[1];
        if(board[i][j] == 'M')
            board[i][j] = 'X';
        else
            bfs(board,i,j);
        return board;
    }

    void bfs(vector<vector<char>>& board,int i,int j)
    {
        queue<pair<int,int>> que;
        que.push({i,j});
        vis[i][j] = true;

        while(que.size())
        {
            auto [a,b] = que.front();
            que.pop();
            
            int count = 0;
            // 统计雷的数量
            for(int k=0;k<8;++k)
            {
                int x=a+dx[k],y=b+dy[k];
                if(x>=0 && x<m && y>=0 && y<n && board[x][y] == 'M') count++;
            }

            if(count){
                board[a][b] = count + '0';
            }
            else{
                board[a][b] = 'B';
                for(int k=0;k<8;++k)
                {
                    int x=a+dx[k],y=b+dy[k];
                    if(x>=0 && x<m && y>=0 && y<n && board[x][y] == 'E')
                    {
                        // 这里改为'B' 避免被其他点统计到 队列中去
                        // 也可以使用 vis[x][y] = ture; 进行标记 不被统计 !!!
                        board[x][y] = 'B'; 
                        que.push({x,y});
                    }
                }
            }
        }
    }
};


本篇到此结束,感谢你的阅读。

祝你好运,向阳而生~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值