本质上是对图的遍历,看图中是否存在环,首先要做的是根据题目中的条件构造图,这里采用邻接表方式,方便便利,然后从每个节点开始遍历,看是否存在环。
class Solution {
private boolean hasCycle = false;
public boolean canFinish(int numCourses, int[][] prerequisites) {
// 构造图
List<Integer>[] graph = buildGraph(numCourses, prerequisites);
// 标记已经访问过的节点
boolean[] visited = new boolean[numCourses];
// 标记当前访问路径上的节点
boolean[] onPath = new boolean[numCourses];
// 从每个节点开始遍历
for (int i = 0; i < numCourses; i++) {
traverse(graph, visited, onPath, i);
}
return !hasCycle;
}
private void traverse(List<Integer>[] graph, boolean[] visited, boolean[] onPath, int index){
if (onPath[index]){
hasCycle = true;
}
if (visited[index] || hasCycle){
return;
}
// 与回溯算法过程类似,深度遍历它所能到达的每一条边
visited[index] = true;
onPath[index] = true;
for (int num : graph[index]){
traverse(graph, visited, onPath, num);
}
onPath[index] = false;
}
private List<Integer>[] buildGraph(int numCourses, int[][] prerequisites){
List<Integer>[] graph = new ArrayList[numCourses];
for (int i = 0; i < numCourses; i++) {
graph[i] = new ArrayList<>();
}
// 对 [0, 1] 而言,表示 课程1需要要课程0之前完成,也就是存在一条从1指向0的边
for (int i = 0; i < prerequisites.length; i++) {
graph[prerequisites[i][1]].add(prerequisites[i][0]);
}
return graph;
}
}