DFS和BFS在拓扑排序中的使用

  • 进行练习的题目是207. 课程表210. 课程表 II

  • 什么是拓扑排序呢?给定一个包含n个节点有向图G,所谓拓扑排序就是给出它的节点编号的一种排序。在排序中需要满足,对于G中任何一条有向边(u, v),u在排序中都出现在v的前面。

  • 简而言之,如果G中有环,那就不存在拓扑排序,同时,拓扑排序也不止一种形式。

使用DFS

  • 思路:对于一个节点,如果它的相邻节点都被访问完成,则回溯到它的时候,它也被访问完成,并将其放入ans。由递归的特性可以知道,此时,在这个点之前的点都已经在ans中了,也就是说,ans的反向就是一个拓扑排序的结果。
  • 访问状态有三种:[0]:未访问,[1]:访问中,[2]:访问完成
  • 所以,在dfs的时候,该点的邻边访问状态为0时,对其访问;为1时,遇到环,返回失败;为2时,不需要动作。
  • 给出210题的DFS解答,207题的解答类似。
class Solution {
private:
    vector<vector<int>> edge;   //用于记录边的信息
    vector<int> visited;        //用于记录访问状态
    bool hasCircle = false;     //用于记录是否有环
    vector<int> ans;
public:
    void dfs(int i) {
        visited[i] = 1;
        for (int num: edge[i]) {
            if (visited[num] == 0) {
                dfs(num);
                if(hasCircle)
                    return;
            } else if (visited[num] == 1) {
                hasCircle = true;
                return;
            }
        }
        visited[i] = 2;
        ans.push_back(i);
    }

    vector<int> findOrder(int numCourses, vector<vector<int>> &prerequisites) {
        //进行vector的初始化,也可以使用resize()
        edge = vector<vector<int>>(numCourses);
        visited = vector<int>(numCourses);
        //首先,将题目给的信息转化为邻接矩阵
        for (auto& pair: prerequisites) {
            //让先修课指向之后的课程
            edge[pair[1]].push_back(pair[0]);
        }
        for (int i(0); i < numCourses; i++)
            if (!visited[i])
                dfs(i);
        reverse(ans.begin(), ans.end());
        if (ans.size() == numCourses)
            return ans;
        else return {};
    }
};

使用DFS

  • BFS是逆向思维:最先被放进去的是在拓扑排序中最后面的节点。也可以使用正向思维,顺序生成拓扑排序的结果,就是DFS了。
  • 思路:在拓扑排序最开始的点,一定是没有入度的,将该节点加入队列。对队列中的元素进行访问后,将其指向的点的入度-1,即代表它的相邻节点少了一个入度(即为没有先修课了),当相邻节点的入度为0时,它就到了最开始的样子。继续这个过程,直到答案中包含所有的节点或者不存在没有入边的节点。
  • 同样的,给出210题的DFS解答
class Solution {
private:
    vector<vector<int>> edges;
    vector<int> indegree;
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        edges = vector<vector<int>>(numCourses);
        indegree = vector<int>(numCourses);
        for (auto& pair: prerequisites) {
            edges[pair[1]].push_back(pair[0]);
            indegree[pair[0]]++;
        }
        queue<int> sto;
        for(int i(0); i < numCourses; i++)
            if (indegree[i] == 0)
                sto.push(i);
        vector<int> ans;
        while (!sto.empty()) {
            int visitedNow = sto.front();
            sto.pop();
            ans.push_back(visitedNow);
            for (int i: edges[visitedNow]) {
                indegree[i]--;
                if(!indegree[i])
                    sto.push(i);
            }
        }
        if (ans.size() == numCourses)
            return ans;
        else return {};
    }
};

复杂度分析

  • BFS做法:时间复杂度和空间复杂度都为O(m + n)。O(m)的空间用来存边的关系,O(n)的空间用来存结果。
  • DFS做法:同上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值