• 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;
        }
    
  • 相关阅读:
    configparser模块
    xml文件解析
    shutil模块 + shelve模块 二合一版
    hashlib模块
    subprocess模块和sys模块
    re模块
    os模块
    random模块
    time模块、datetime模块讲解
    洛谷P3414 SAC#1
  • 原文地址:https://www.cnblogs.com/Frank-Hong/p/13326754.html
Copyright © 2020-2023  润新知