题目叙述
必须选修 numCourse 门课程,记为 0 到 numCourse-1 。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1]
给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习?
实例:输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。
思考
个人错误的思考记录 (看官可跳过直接进入下面正确解答)
本题又是一道典型的dfs,我另外设置一个成绩数组,默认为0,访问一个那么改成1,那么如果找到为1的表示此路不通;为了记录位置,另外用一个 Map来存储 索引位置。
- 错误在于没有真正抽象这道题,这道题可以抽象成 判断一个有向图是否有环,因为以同一门课程开头的数组不一定只有一个,比如 [1,0],[1,3];那么我上面的解法就错误了。
正确思考
既然是一张图,我们就用三种状态记录:
安排标记数组 courses[]
courses[i] == 0 没有被访问到,继续
courses[i] == 1 在本节点启动的搜索访问过一次,现在第二次遇到,说明有环,返回false
courses[i] == -1 在另外节点启动的遍历中被访问过,说明不需要继续搜索,返回true.
尊重原创,学习参考地址.
代码
public boolean canFinish(int numCourses, int[][] prerequisites) {
List<List<Integer>> adj = new ArrayList<>();
int[] courses = new int[numCourses];
for(int i=0; i<numCourses; i++){
adj.add(new ArrayList<>());
}
// 将图的每一次起点不同的遍历变成一个链表
for(int[] pre:prerequisites){
adj.get(pre[1]).add(pre[0]);
}
for(int i=0; i<numCourses; i++){
// 主方法中,对dfs 进行判断确认返回值,而不是直接返回
if(!dfs(adj,courses,i)) return false;
}
return true;
}
// dfs 写法模板
public boolean dfs(List<List<Integer>> adj,int[] courses,int index){
// 确定失败返回的条件
if(courses[index] == 1){
return false;
}
// 确认成功的条件
if(courses[index] == -1){
return true;
}
// 做标记,修改值
courses[index] = 1;
for(Integer j: adj.get(index)){
if(!dfs(adj,courses,j)) return false;
}
// 遍历结束,这一题不是改回原值,而是用另一种状态记录。
courses[index] = -1;
return true;
}