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> edge;   //用于记录边的信息
    vector visited;        //用于记录访问状态
    bool hasCircle = false;     //用于记录是否有环
    vector 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 findOrder(int numCourses, vector> &prerequisites) {
        //进行vector的初始化,也可以使用resize()
        edge = vector>(numCourses);
        visited = vector(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> edges;
    vector indegree;
public:
    vector findOrder(int numCourses, vector>& prerequisites) {
        edges = vector>(numCourses);
        indegree = vector(numCourses);
        for (auto& pair: prerequisites) {
            edges[pair[1]].push_back(pair[0]);
            indegree[pair[0]]++;
        }
        queue sto;
        for(int i(0); i < numCourses; i++)
            if (indegree[i] == 0)
                sto.push(i);
        vector 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做法:同上。

你可能感兴趣的:(刷题总结)