DFS与BFS LeetCode 刷题小结(一)

2020-03-05  本文已影响0人  思想永不平凡

本节我们将汇总一些 LeetCode bfs与dfs相关的题。



关于深度优先搜索(DFS)和广度优先搜索(BFS),在往期博客中有所介绍,本节我们将介绍其他典题。

朋友圈

班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。

给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。

来源:https://leetcode-cn.com/problems/friend-circles

image.png

给出的数据是一个图的邻接矩阵,求得是该图无向图连通块的个数,对于这类问题,bfs和dfs都可以解决。

DFS版

我们需要一个vist来保存节点状态:

vector<bool> vist(M.size(),false);

当我们访问一个节点时,访问与它相邻的节点,再访问这个节点的相邻节点,直到访问到“底”,每访问一个节点改变它的状态,当遍历到没有访问的相邻节点时,回溯之前的节点继续访问。
程序如下:

class Solution {
public:
    int findCircleNum(vector<vector<int>>& M) {
        int len =M.size();
        vector<bool> vist(len,false);
        int res =0;
        for(int i=0;i<len;i++){
            if(!vist[i]){
                dfs(M,vist,i);
                res++;
            }
        }
        return res;
    }
    void dfs(vector<vector<int>>& M,vector<bool>& vist,int i){
        for(int j=0;j<M.size();j++){
            if(M[i][j]==1&&!vist[j]){
                vist[j]=true;
                dfs(M,vist,j);
            }
        }
    }
};

BFS版

广度优先就是一层层地来,类似于树的层次遍历,需要用到队列保存每层节点,如果对树的层序遍历很熟悉,那图的广度优先搜索也就不难了。
程序如下:

class Solution {
public:
    int findCircleNum(vector<vector<int>>& M) {
        int len = M.size();
        int res=0;
        vector<bool> vist(len,false);
        queue<int> q;
        for(int i=0;i<len;i++){
            if(!vist[i]){
                q.push(i);
                while(!q.empty()){
                    int cur = q.front();
                    q.pop();
                    vist[cur]=true;
                    for(int j=0;j<len;j++){
                        if(M[cur][j]==1&&!vist[j]){
                            q.push(j);
                        }
                    }
                }
                res++;
            }
        }
        return res;
    }
};

这个题还有一种做法是并查集,本节我们暂不讲述,在之后的章节会汇总并查集有关的题。

岛屿数量

给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。

来源:https://leetcode-cn.com/problems/number-of-islands

image.png

这个题的思路和上面题类似,需要注意的地方便是处理的对象略有不同,上题是邻接矩阵,来看具体的细节吧。

DFS版

如果一个区域块是1,是一块陆地,我们要找到与它相邻的1,在不越界的情况下,该块上下左右若为1,则递归该块继续判断其上下左右块,如果递归结束了,意味着是一块陆地,因为一块陆地的任意两个点明显是可达的(通过上下左右移动),在递归时,每到为1的块,该块置零。
程序如下:

class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        int rows = grid.size();
        if(rows==0){
            return 0;
        }
        int lists = grid[0].size();

        int lands = 0;
        for(int i=0;i<rows;i++){
            for(int j=0;j<lists;j++){
                if(grid[i][j]=='1'){
                    lands++;
                    dfs(grid,i,j);
                }
            }
        }
        return lands;
    }
    void dfs(vector<vector<char>>& grid,int i,int j){
        int rows=grid.size();
        int lists=grid[0].size();

        grid[i][j]='0';
        if(i>=1 && grid[i-1][j]=='1'){
            dfs(grid,i-1,j);
        }
        if(i<rows-1 && grid[i+1][j]=='1'){
            dfs(grid,i+1,j);
        }
        if(j>=1 && grid[i][j-1]=='1'){
            dfs(grid,i,j-1);
        }
        if(j<lists-1&&grid[i][j+1]=='1'){
            dfs(grid,i,j+1);
        }
    }
};

BFS版

BFS版其实也大同小异,当处于陆地块时,该块置零,用一个队列保存其上下左右的陆地块,对于队列里面的块,在不越界的情况下判断其上下左右,为1的块入队列,同时赋0。
程序如下:

class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        int rows= grid.size();
        if(rows==0){
            return 0;
        }
        int lists=grid[0].size();

        int lands=0;
        for(int i=0;i<rows;i++){
            for(int j=0;j<lists;j++){
                if(grid[i][j]=='1'){
                    lands++;
                    grid[i][j]='0';
                    queue<pair<int,int>> point;
                    point.push({i,j});
                    while(!point.empty()){
                        auto cur = point.front();
                        point.pop();
                        int i_=cur.first;
                        int j_=cur.second;
                        if(i_>=1 && grid[i_-1][j_]=='1'){
                            point.push({i_-1,j_});
                            grid[i_-1][j_]='0';
                        }
                        if(i_<rows-1 && grid[i_+1][j_]=='1'){
                            point.push({i_+1,j_});
                            grid[i_+1][j_]='0';
                        }
                        if(j_>=1 && grid[i_][j_-1]=='1'){
                            point.push({i_,j_-1});
                            grid[i_][j_-1]='0';
                        }
                        if(j_<lists-1 && grid[i_][j_+1]=='1'){
                            point.push({i_,j_+1});
                            grid[i_][j_+1]='0';
                        }
                    }
                }
            }
        }
        return lands;
    }
};

最后来看一道bfs的题。

腐烂的橘子

在给定的网格中,每个单元格可以有以下三个值之一:

值 0 代表空单元格;
值 1 代表新鲜橘子;
值 2 代表腐烂的橘子。
每分钟,任何与腐烂的橘子(在 4 个正方向上)相邻的新鲜橘子都会腐烂。

返回直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1。

来源:https://leetcode-cn.com/problems/rotting-oranges

image.png

这个题很明显使用广度优先搜索,从烂橘子出发,在不越界的情况下,其上下左右的橘子都会烂掉,计时,统计这个过程烂掉的橘子数,若等于原始好的橘子数,返回时间,否则为-1。
程序如下:

class Solution {
public:
    int orangesRotting(vector<vector<int>>& grid) {
        int min=0,fresh=0;
        queue<pair<int,int>> q;
        for(int i=0;i<grid.size();i++){
            for(int j=0;j<grid[0].size();j++){
                if(grid[i][j]==1){
                    //新鲜橘子
                    fresh++;
                }else if(grid[i][j]==2){
                    //腐烂的橘子
                    q.push({i,j});
                }
            }
        }
        vector<pair<int,int>> dirs={{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
        while(!q.empty()){
            int n=q.size();
            bool sign=false;
            while(n--){
                auto point=q.front();
                q.pop();
                for(auto cur : dirs){
                    int i=point.first+cur.first;
                    int j=point.second+cur.second;
                    if(i>=0&&i<grid.size()&&j>=0&&j<grid[0].size()&&grid[i][j]==1){
                        grid[i][j]=2;
                        q.push({i,j});
                        fresh--;
                        sign=true;
                    }
                }
            }
            if(sign){
                min++;
            }
        }
        return fresh?-1:min;
    }
};

本节内容暂告一段落,之后将继续更新。

上一篇下一篇

猜你喜欢

热点阅读