import java.util.ArrayList;
import java.util.List;
/**
* 你这个学期必须选修 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
*
*/
public class CanFinish {
public static void main(String[] args) {
int[][] prerequisites = {{9, 6}, {9, 7}, {10, 7}, {10, 8}, {8, 5}, {7, 4}, {6, 3}, {5, 2}, {4, 1}, {4, 2},
{3, 1}, {2, 0}, {1, 0}};
CanFinishSolution s = new CanFinishSolution();
boolean b = s.canFinish(11, prerequisites);
System.out.println(b);
}
}
class CanFinishSolution {
List<List<Integer>> courses;
int[] visited;
boolean valid = true;
public void init(int numCourses, int[][] prerequisites) {
courses = new ArrayList<List<Integer>>();
for (int i = 0; i < numCourses; i++) {
courses.add(new ArrayList<Integer>());
}
for (int[] info : prerequisites) {
courses.get(info[1]).add(info[0]);
}
visited = new int[numCourses];
}
public boolean canFinish(int numCourses, int[][] prerequisites) {
if (numCourses < 1 || numCourses > Math.pow(10, 5)) {
return false;
}
init(numCourses, prerequisites);
for (int i = 0; i < numCourses; i++) {
if (visited[i] == 0) {
// visited == 0 表示未访问的节点
dfs(i);
}
}
return valid;
}
private void dfs(int u) {
visited[u] = 1; // visited == 1 表示节点访问中
for (int v : courses.get(u)) {
if (visited[v] == 0) {
// 如果找到的节点没有被访问,则继续查找此节点的下一跳;
dfs(v);
if (!valid) {
return;
}
} else if (visited[v] == 1) {
// 如果找到的节点正在访问中,则属于有向环图
valid = false;
return;
}
// 剩下的情况只有已经访问完毕,可以不用处理
}
visited[u] = 2; // visited == 2 表示节点访问完毕
}
}
运行结果:
true