leetcode 207. 课程表

问题描述

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

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

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

输入: 2, [[1,0]] 
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。
示例 2:

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

提示:

输入的先决条件是由 边缘列表 表示的图形,而不是 邻接矩阵 。详情请参见图的表示法。
你可以假定输入的先决条件中没有重复的边。
1 <= numCourses <= 10^5

代码

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        int n = prerequisites.size(),i;
        vector<vector<int>> G(numCourses);
        vector<bool> visit(numCourses,0);
        for(auto j:prerequisites)
        {
            G[j[1]].push_back(j[0]);
        }
        for(i = 0; i < numCourses; ++i)
        {
            if(dfs(G,i,visit))
                return false;
        }
        return true;
    }
    bool dfs(vector<vector<int>> &G,int cur,vector<bool>& visit)
    {
        if(visit[cur])return true;//如果被访问过说明已经找到环了
        visit[cur] = true;
        for(auto j:G[cur])
        {
            if(dfs(G,j,visit))
                return true;
        }
        visit[cur] = false;
        return false;
    }
};

结果:

执行用时 :292 ms, 在所有 C++ 提交中击败了5.62%的用户
内存消耗 :13.2 MB, 在所有 C++ 提交中击败了75.00%的用户

代码

我们在递归过程中标记已经访问并且不是环中的节点为状态-1用于剪枝以节省时间:

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        int n = prerequisites.size(),i;
        vector<vector<int>> G(numCourses);
        vector<int> visit(numCourses,0);
        for(auto j:prerequisites)
        {
            G[j[1]].push_back(j[0]);
            //当然G[j[0]].push_back(j[1])也能找到环,但是比如【0,1】,0的先修课程是1,方向应该是1->0
        }
        for(i = 0; i < numCourses; ++i)
        {
            if(dfs(G,i,visit))
                return false;
        }
        return true;
    }
    bool dfs(vector<vector<int>> &G,int cur,vector<int>& visit)
    {
        if(visit[cur]==1)return true;//如果被访问过说明已经找到环了
        else if(visit[cur] == -1)return false;//-1代表之前访问过没有找到环,这里就不在继续递归以节省时间
        visit[cur] = 1;
        for(auto j:G[cur])
        {
            if(dfs(G,j,visit))
                return true;
        }
        visit[cur] = -1;
        return false;
    }
};

结果:

执行用时 :44 ms, 在所有 C++ 提交中击败了45.89%的用户
内存消耗 :13.2 MB, 在所有 C++ 提交中击败了62.50%的用户

代码3

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        int n = prerequisites.size(),i;
        vector<vector<int>> G(numCourses);
        vector<int> in(numCourses,0);
        queue<int> q;
        for(auto j:prerequisites)
        {
            G[j[1]].push_back(j[0]);//方向j[1]->j[0],所以是j[0]的入度
            ++in[j[0]];
        }
        //删除掉初始入度为0的点
        for(i = 0; i < numCourses; ++i)
        {
            if(in[i] == 0)q.push(i);
        }
        while(!q.empty())
        {
            int t = q.front();
            q.pop();
            for(auto w:G[t])
            {
                //将点t从有向图中删除,因此与t相连得点的入度都要减一,并找到新的入度为0的点
                if(--in[w]==0)q.push(w);
            }
        }
        //如果还存在入度非零的点说明存在环
        for(i = 0; i < numCourses; ++i)
        {
            if(in[i])return false;
        }
        return true;
    }
};

结果:

执行用时 :44 ms, 在所有 C++ 提交中击败了45.89%的用户
内存消耗 :12.7 MB, 在所有 C++ 提交中击败了93.75%的用户
posted @ 2020-06-10 16:09  曲径通霄  阅读(211)  评论(0编辑  收藏  举报