207.课程表

你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 。

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

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

示例2:

输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成​课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。

方法一:BFS

分析:

这道题即判断一个有向图中是否有环,可以用拓扑排序算法来解决。过程如下:
1.从有向图中选择没有前驱(入度为0)的顶点输出。
2.依次删除1中的顶点,并删除这些顶点发出的全部边。
3.重复上述2步,直到图中不存在没有前驱的顶点为止。若此时还有顶点剩余则说明图中有环。
这道题还需要考虑的一个关键问题是如何表示图。
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        //建立图,graph[i]包含所有结点i指向的结点,有点类似图的邻接表存储结构
        vector<vector<int>> graph(numCourses,vector<int>());
        //记录每个结点的入度
        vector<int> in(numCourses,0);
        for(auto i:prerequisites){
            graph[i[1]].push_back(i[0]);
            in[i[0]]++;
        }
        queue<int> q;
        for(int i=0;i<numCourses;++i){
            if(in[i]==0)q.push(i);
        }
        while(!q.empty()){
            int front=q.front();
            q.pop();
            for(auto i:graph[front]){
                in[i]--;
                if(in[i]==0) q.push(i);
            }
        }
        //如果还有结点的入度不为0,则说明图中存在环
        for(int i=0;i<numCourses;++i)
            if(in[i]!=0) return false;
        return true;
    }

方法二:DFS

分析:

需要使用一个访问数组来记录当前节点的访问情况。访问数组的取值:
0:未被访问
1:已经被访问,即完成回溯
-1:正在被访问,还未回溯完成
具体过程:
深度优先遍历访问每个节点。在使用深度优先遍历访问数组的时候先将当前节点的访问状态置为-1,表示正在访问该节点。然后递归的去访问该节点所有指向的节点。最后将该节点的访问状态置为1,表示已经访问过了。在递归访问节点的过程中只要发现当前待访问节点的访问状态为-1,则表示出现了环;若访问状态为1,则无需在访问该节点。
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<vector<int>> graph(numCourses, vector<int>());
        vector<int> visit(numCourses);
        for (auto a : prerequisites) {
            graph[a[1]].push_back(a[0]);
        }
        //注意这里是有向图,当且仅当所有结点都能回溯到自身位置才能说明图中不存在环
        for (int i = 0; i < numCourses; ++i) {
            if (!canFinishDFS(graph, visit, i)) return false;
        }
        return true;
    }
    //返回值表示从结点i开始是否能完成深度优先搜索并回溯到原点
    bool canFinishDFS(vector<vector<int>>& graph, vector<int>& visit, int i) {
        //当某个结点还未完成回溯时又被访问,说明图中存在环
        if (visit[i] == -1) return false;
        //因为canFinish函数中对所有结点都调用了该函数,因此在此之前可能该结点被访问过且能够回溯到原点,加入这一行可减少运行时间
        if (visit[i] == 1) return true;
        //正在被访问
        visit[i] = -1;
        for (auto a : graph[i]) {
            if (!canFinishDFS(graph, visit, a)) return false;
        }
        //回溯到原点表示访问完成
        visit[i] = 1;
        return true;
    }
原文地址:https://www.cnblogs.com/Frank-Hong/p/13326754.html