leetcode-8:DFS和BFS

DFS和BFS

节点间通路

节点间通路。给定有向图,设计一个算法,找出两个节点之间是否存在一条路径。

提示:

节点数量n在[0, 1e5]范围内。
节点编号大于等于 0 小于 n。
图中可能存在自环和平行边。

思路: 判断两点之间是否连通,用DFS或者BFS,用栈实现DFS,用队列实现BFS。我不喜欢递归,递归要迭代,我脑子里建不起迭代的过程图。

用栈实现DFS伪码:

  1. 栈初始化,并让起始顶点入栈,起始顶点改为已访问

  2. 栈不为空:

    2.1 取栈顶元素
    2.2 栈顶元素的邻接点 n n n,并且 n n n没有被访问,则:将邻接点 n n n标记为已访问,并且让点 n n n进栈
    2.3 如果2.2没有邻接点进展,需要将当前结点退栈。

class Solution {
public:
    vector<vector<int>> construct_map(int n, vector<vector<int>>& graph){
        vector<vector<int>> map(n);
        int row = graph.size();
        for(int i = 0;i<row;i++){
            int node_1 = graph[i][0];
            int node_2 = graph[i][1];
            map[node_1].push_back(node_2);
        }
        return map;
    }
    // DFS with stack
    bool findWhetherExistsPath(int n, vector<vector<int>>& graph, int start, int target) {
        if(start == target) return true;
        vector<vector<int>> map = construct_map(n, graph);
        // 
        vector<int> visit(n, 0);
        stack<int> S;
        S.push(start);
        visit[start] = 1;
        while(!S.empty()){
            int tmp = S.top();
            int len = map[tmp].size();
            bool flag = false;
            for(int i=0;i<len;i++){
                int node = map[tmp][i];
                if(!visit[node]){
                    S.push(node);
                    visit[node] = 1;
                    flag = true;
                    if(node == target){
                        return true;
                    }
                    break;
                }
            }
            if(!flag){
                S.pop();
            }
        }
        return false;
    }
    // BFS with queue
    bool findWhetherExistsPath(int n, vector<vector<int>>& graph, int start, int target) {
        if(start == target) return true;
        vector<vector<int>> map = construct_map(n, graph);
        // 
        vector<int> visit(n, 0);
        queue<int> Q;
        Q.push(start);
        visit[start] = 1;
        while(!Q.empty()){
            int tmp = Q.front();
            int len = map[tmp].size();
            bool flag = false;
            for(int i=0;i<len;i++){
                int node = map[tmp][i];
                if(!visit[node]){
                    visit[node] = 1;
                    Q.push(node);
                    flag = true;
                    if(node == target){
                        return true;
                    }
                }
            }
            if(!flag){
                Q.pop();
            }
        }
        return false;
    }
};

判断二分图

给定一个无向图graph,当这个图为二分图时返回true。

如果我们能将一个图的节点集合分割成两个独立的子集A和B,并使图中的每一条边的两个节点一个来自A集合,一个来自B集合,我们就将这个图称为二分图。

graph将会以邻接表方式给出,graph[i]表示图中与节点i相连的所有节点。每个节点都是一个在0到graph.length-1之间的整数。这图中没有自环和平行边: graph[i] 中不存在i,并且graph[i]中没有重复的值。

思路: 二分图染色法判定二分图。对一个无向图,要给每个图上顶点进行染色,最多用两种染色,任意相邻的顶点染色不同,如果可以对图进行染色,那就说明是一个二分图。题目中没说给给定的图是连通图,所以还需要考虑到非连通的图。

class Solution {
public:
    bool isBipartite(vector<vector<int>>& graph) {
        int n = graph.size();
        stack<int> S;
        vector<int> vis(n, 0);
        vector<int> color(n, -1);
        // 考虑图非连通
        for(int i=0;i<n;i++){
            if(color[i]==-1){
                S.push(i);
                vis[i] = 1;
                color[i] = 1;
                while(!S.empty()){
                    int tmp = S.top();
                    int len = graph[tmp].size();
                    bool flag = false;
                    for(int i=0;i<len;i++){
                        int node = graph[tmp][i];
                        if(vis[node]){
                            if(color[node] == color[tmp]) return false;
                        }else{
                            S.push(node);
                            vis[node] = 1;
                            flag = true;
                            color[node] = color[tmp] ^ 1;
                             break;
                        }
                    }
                    if(!flag) S.pop();
                }
            }
        }
        return true;
    }
};

课程表

现在你总共有 n 门课需要选,记为 0 到 n-1。

在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]

给定课程总量以及它们的先决条件,判断是否可能完成所有课程的学习?

思路: 判断是否是有向无环图DAG。DAG才有拓扑排序,非DAG没有拓扑排序。使用顶点入度+BFS,判断是否能够拓扑排序。

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        int row = prerequisites.size();
        vector<int> degree(numCourses, 0);
        for(int i=0;i<row;i++){
            degree[prerequisites[i][1]]++;
        }
        queue<int> Q;
        for(int i=0;i<numCourses;i++){
            if(!degree[i]){
                Q.push(i);
            }
        }
        while(!Q.empty()){
            int tmp = Q.front();
            for(int i=0;i<row;i++){
                if(tmp == prerequisites[i][0]){
                    int node = prerequisites[i][1];
                    degree[node]--;
                    if(degree[node]==0){
                        Q.push(node);
                    }
                }
            }
            Q.pop();
            numCourses--;
        }
        if(numCourses==0) return true;
        else return false;
    }
};

不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

问总共有多少条不同的路径?

思路: 假设网格的大小为 n ∗ m n*m nm,规定只能向下或者向右,从网格的左上角到右下角需要走 n + m − 2 n+m-2 n+m2步,其中向下走了 n − 1 n-1 n1,向右走了 m − 1 m-1 m1,所以总的路径数就是 C m + n − 2 m i n ( n , m ) − 1 C_{m+n-2}^{min(n,m)-1} Cm+n2min(n,m)1

求组合数用到了 C n m = C n − 1 m − 1 + C n − 1 m C_{n}^{m} = C_{n-1}^{m-1} + C_{n-1}^{m} Cnm=Cn1m1+Cn1m

class Solution {
public:
    int uniquePaths(int m, int n) {
        int total = m + n - 2;
        int select = min(m, n) - 1;
        return combination(total, select);
    }
    int combination(int total, int select){
        if(total==select||select==0){
            return 1;
        }else if(select == 1){
            return total;
        }
        return combination(total-1, select-1) + combination(total-1, select);
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

马鹤宁

谢谢

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值