现在你总共有 n 门课需要选,记为 0 到 n-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。这是不可能的。
其实就是构造有向图,找其中有没有图的问题
首先用二维矩阵构造图,对于每个节点,记录其指向的其他节点的边
然后对于每个节点,从其节点出发,用DFS搜索是否构成环,每访问到一个节点标记一下,当访问到某一节点发现已经被标记,那么说明就是存在环路。
public boolean canFinish(int numCourses, int[][] prerequisites) {
if (numCourses <= 1) {
return true;
}
List<Integer>[]A = new List[numCourses];
for(int i=0;i<numCourses;i++){
A[i] = new ArrayList<>();
}
for(int i=0;i<prerequisites.length;i++){
A[prerequisites[i][0]].add(prerequisites[i][1]);
}
boolean[] mark = new boolean[numCourses];
for(int i=0;i<numCourses;i++){
if(!A[i].isEmpty()){
if(cycle(i,mark,A)){
return false;
}
}
}
return true;
}
boolean cycle(int curNum, boolean[] mark, List<Integer>[]A){
if(mark[curNum]){
return true;
}
mark[curNum] = true;
for(Integer i : A[curNum]){
if(cycle(i,mark,A)){
return true;
}
}
mark[curNum] = false;
return false;
}
改进:递归过程中增加安全点的判断
public boolean canFinish(int numCourses, int[][] prerequisites) {
if (numCourses <= 1) {
return true;
}
List<List<Integer>>A = new ArrayList<>();
for(int i=0;i<numCourses;i++){
A.add( new ArrayList<>());
}
for(int i=0;i<prerequisites.length;i++){
A.get(prerequisites[i][0]).add(prerequisites[i][1]);
}
int[] mark = new int[numCourses];
for(int i=0;i<numCourses;i++){
if(!A.get(i).isEmpty()){
if(cycle(i,mark,A)){
return false;
}
}
}
return true;
}
boolean cycle(int curNum, int[] mark, List<List<Integer>>A){
if(mark[curNum] == 1){
return true;
}else if(mark[curNum] == 2){
return false;
}
mark[curNum] = 1;
for(Integer i : A.get(curNum)){
if(cycle(i,mark,A)){
return true;
}
}
//标记为2,表示是安全的点,下次遇到直接返回false就可以
mark[curNum] = 2;
return false;
}