import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
// 入度表(广度优先遍历)
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
// 入度表indegrees
int[] indegrees = new int[numCourses];
List<List<Integer>> adjacency = new ArrayList<>();
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < numCourses; i++) {
adjacency.add(new ArrayList<>());
}
// Get the indegree and adjacency of every course.
for (int[] cp : prerequisites) {
indegrees[cp[0]]++;
adjacency.get(cp[1]).add(cp[0]);
}
// 将所有入度为0的节点入队
for (int i = 0; i < numCourses; i++) {
if (indegrees[i] == 0) {
queue.add(i);
}
}
// BFS
// 当queue非空时,依次将队首节点出队,在课程安排图中删除此节点pre
while (!queue.isEmpty()) {
// 在每次pre出队时,执行numCourses--
int pre = queue.poll();
numCourses--;
for (int cur : adjacency.get(pre)) {
// 并不是真正从邻接表中删除此节点pre,而是将此节点对应所有邻接节点cur的入度-1,即 indegrees[cur]-=1
// 当入度-1后邻接节点cur的入度为0,说明cur所有的前驱节点已经被 “删除”,此时将cur入队
if (--indegrees[cur] == 0) {
queue.add(cur);
}
}
}
// 若整个课程安排图是有向无环图(即可以安排),则所有节点一定都入队并出队过,即完成拓扑排序。换个角度说,若课程安排图中存在环,一定有节点的入度始终不为0
// 因此,拓扑排序出队次数等于课程个数,返回numCourses == 0判断课程是否可以成功安排
return numCourses == 0;
}
}
// 深度优先遍历: 原理是通过 DFS 判断图中是否有环
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
List<List<Integer>> adjacency = new ArrayList<>();
for (int i = 0; i < numCourses; i++) {
adjacency.add(new ArrayList<>());
}
// 借助一个标志列表flags,用于判断每个节点i(课程)的状态:
// 未被 DFS 访问:i == 0;
// 已被其他节点启动的 DFS 访问:i == -1;
// 已被当前节点启动的 DFS 访问:i == 1。
int[] flags = new int[numCourses];
for (int[] cp : prerequisites) {
adjacency.get(cp[1]).add(cp[0]);
}
// 对numCourses个节点依次执行DFS,判断每个节点起步DFS是否存在环,若存在环直接返回False
for (int i = 0; i < numCourses; i++) {
if (!dfs(adjacency, flags, i)) {
return false;
}
}
// 若整个图DFS结束并未发现环,返回True
return true;
}
private boolean dfs(List<List<Integer>> adjacency, int[] flags, int i) {
// 当flag[i]==1,说明在本轮DFS搜索中节点i被第2次访问,即课程安排图有环,直接返回False
if (flags[i] == 1) return false;
// 当flag[i]==-1,说明当前访问节点已被其他节点启动的DFS访问,无需再重复搜索,直接返回True
if (flags[i] == -1) return true;
// 将当前访问节点i对应flag[i]置1,即标记其被本轮DFS访问过;
flags[i] = 1;
// 递归访问当前节点i的所有邻接节点j,当发现环直接返回False
for (Integer j : adjacency.get(i)) {
if (!dfs(adjacency, flags, j)) {
return false;
}
}
// 当前节点所有邻接节点已被遍历,并没有发现环,则将当前节点flag置为-1并返回True
flags[i] = -1;
return true;
}
}
leetcode207. 课程表
最新推荐文章于 2024-05-21 20:40:10 发布