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%的用户