## 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/course-schedule
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
private List<List<Integer>> edges;
private byte[] visited;
private Stack<Integer> stack;
private boolean ans = true;
public boolean canFinish(int numCourses, int[][] prerequisites) {
edges = new ArrayList<>();
stack = new Stack<>();
visited = new byte[numCourses];
for (int i = 0; i < numCourses; i++) {
edges.add(new LinkedList<>());
}
for (int[] prerequisite : prerequisites) {
edges.get(prerequisite[1]).add(prerequisite[0]);
}
for (int i = 0; i < edges.size(); i++) {
if (visited[i] == 0) {
dfs(i);
}
}
return ans;
}
private void dfs(int i) {
visited[i] = 2;
List<Integer> integers = edges.get(i);
if (integers.size() == 0) {
visited[i] = 1;
return;
}
for (Integer integer : integers) {
if (visited[integer] == 0) {
dfs(integer);
} else if (visited[integer] == 2) {
ans = false;
return;
}
}
visited[i] = 1;
}
}
e.g.总共有7门课程,每门课程修完后可解锁的课程图如下
A->C;A->F;A->E; 0->{2,4,5}
B->A;B->C; 1->{0,2}
C 2->{}
D->F 3->{5}
E 4->{}
F->E;F->G 5->{4,6}
G 6->{}
1.我一开始不完善的解法思路
(1).获取课程的图表示,即上完本课程可接下来上什么课程
(2).设置一个visited数组用于保存每一个课程的状态,0->未学习;1->学习完成;2->学习中
(3).依次遍历每一个课程,从0->6,若该课程已经学习过了,就直接跳过该课程
(4).每遍历一个课程,设置该课程为正在学习中,然后获取该课程对应的解锁课程,再依次遍历每一个解锁的课程
(5).若当前课程没有可解锁的课程,说明当前课程已经学完了,设置当前课程状态为1;若当前课程可解锁的课程全部都解锁完成了,设置当前课程为1,表示当前课程也全部都学完了。
(6).若最终程序陷入了死循环,抛出了栈溢出错误,说明图中有环;
若最终程序正常结束,说明该题有解;
2.我后来较完善的思路
(1).获取课程的图表示,即上完本课程可接下来上什么课程
(2).设置一个visited数组用于保存每一个课程的状态,0->未学习;1->学习完成;2->学习中
(3).依次遍历每一个课程,从0->6,若该课程已经学习过了,就直接跳过该课程
(4).每遍历一个课程,设置该课程为正在学习中,然后获取该课程对应的解锁课程,再依次遍历每一个解锁的课程
(PS:若在遍历过程中发现居然还有正在学习的过程,说明图中有环,直接退出程序,返回false)
(5).若当前课程没有可解锁的课程,说明当前课程已经学完了,设置当前课程状态为1;若当前课程可解锁的课程全部都解锁完成了,设置当前课程为1,表示当前课程也全部都学完了。
(6).若最终程序陷入了死循环,抛出了栈溢出错误,说明图中有环;若最终程序正常结束,说明该题有解;
3.具体思路
正例:
A->C;A->F;A->E; 0->{2,4,5}
B->A;B->C; 1->{0,2}
C 2->{}
D->F 3->{5}
E 4->{}
F->E;F->G 5->{4,6}
G 6->{}
1.构造图,请直接阅读上述源码
2.依次遍历每一个节点,此时visited=[0,0,0,0,0,0,0]
1.0.i=0;dfs(0) ,
visited[0] == 0;//因此进入该课程的回溯dfs(0);
visited[0] = 2;//设置该课程状态为正在学习中,此时visited=[2,0,0,0,0,0,0]
List<Integer>integers = edges.get(0);//integers = 3,在依次遍历这3个课程 0->{2,4,5}
visited[2] ==0//课程2尚未学习过,回溯该课程
1.1 i=2;dfs(2);
visited[2] = 2;//visited=[2,0,2,0,0,0,0]
List<Integer>integers = edges.get(2);//2->{},可解锁课程为空,说明该课程可学习完成,置1 return返回,visited=[2,0,1,0,0,0,0];
1.2 i=4;dfs(4)
visited[4] = 2;//[2,0,1,0,2,0,0];
List<Integer>integers = edges.get(4);//4->{},visited=[2,0,1,0,1,0,0];
1.3 i=5;dfs(5);
visited[5] = 2;//visited=[2,0,1,0,1,2,0];
List<Integer>integers = edges.get(5);//5->{4,6};
1.3.1 dfs(4);//课程4已经学习过了
1.3.2. dfs(6);//
visited[6]=2;//visited=[2,0,1,0,1,2,2];
List<Integer>integers = edges.get(6);//6->{},visited=[2,0,1,0,1,2,1];
//因为课程5可解锁的所有课程都已经学过了 5->{4,6};,因此visited[5] =1;visited=[2,0,1,0,1,1,1];
//因为课程0可解锁的所有课程都已经学过了0->{2,4,5},因此 visited[0] = 1; visited=[1,0,1,0,1,1,1];
2.0.i=1;dfs(1)
visited[1] ==0;//该课程未被学习过,进入回溯
visted[1] = 2;//visited=[1,2,1,0,1,1,1];
List<Integer>integers = edges.get(1);//1->{0,2}
dfs(0);//课程0已经学习过了,跳过
dfs(2);//课程2已经学习过了,跳过
//由于课程1解锁的课程都已经修过了,因此课程1自动修完,此时 visited=[1,1,1,0,1,1,1]
3.0.i=2;dfs(2);//已经修过了,跳过
4.0.i=3;dfs(3);
visited[3] ==0;
visited[3] = 2;//此时visited=[1,1,1,2,1,1,1]
List<Integer>integers = edges.get(3);//3->{5}
dfs(5);//该课程已经学习过了,return;
//由于课程3可解锁的课程均已修完,故课程3默认修完,此时visited=[1,1,1,1,1,1,1]
5.0.i=4;dfs(4);//该课程已经修完
6.0;i=5;dfs(5);该课程已经修完
7.0;i=6;dfs(6);该课程已经修完
//该图无环,故可修完所有的课程
反例:
A->{B} 0->{1}
B->{C} 1->{2}
C->{D} 2->{0}
1.构造图,参考源码
2.依次遍历每一个节点,visited = [0,0,0]
1.0.i=0,dfs(0)
visited[0] = 2;//visited = [2,0,0]
List<Integer>integers = edges.get(0)//0->{1};
dfs(1);
visited[1] = 2;//visited = [2,2,0]
List<Integer>integers = edges.get(0)//1->{2};
dfs(2);
visited[2] = 2;//visited = [2,2,2]
List<Integer>integers = edges.get(2)//2->{0};
//此时可发现,陷入了无限循环
dfs(0);
此时判断发现visited[0]==2;//因此可知道图中有环,故直接退出程序,返回false;