84 lines
3.3 KiB
Java
84 lines
3.3 KiB
Java
package graph;
|
||
|
||
import java.util.ArrayList;
|
||
import java.util.LinkedList;
|
||
import java.util.List;
|
||
import java.util.Queue;
|
||
|
||
/**
|
||
* 题目: 207. 课程表 (canFinish)
|
||
* 描述:你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
|
||
* 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
|
||
* 例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
|
||
* 请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
|
||
|
||
* 链接:https://leetcode.cn/problems/course-schedule/
|
||
|
||
*/
|
||
//思路知道,代码不好写
|
||
/*
|
||
思路解析
|
||
构建图与入度数组
|
||
用一个邻接表 graph 表示课程之间的依赖关系。
|
||
对于先修课程对 [ai, bi],表示修课程 ai 之前必须完成课程 bi,因此建立一条从 bi 到 ai 的边,并将 ai 的入度加 1。
|
||
|
||
初始化队列
|
||
将所有入度为 0 的课程(即没有先修要求的课程)加入队列,作为拓扑排序的起点。
|
||
|
||
拓扑排序
|
||
从队列中取出一个课程,将其计入已完成课程数量 count。
|
||
遍历该课程的所有后续课程,将这些课程的入度减 1。如果某个课程的入度减为 0,则说明它所有的先修课程都已完成,将其加入队列。
|
||
|
||
判断是否存在环
|
||
如果最终能处理的课程数量等于总课程数,则说明不存在环路,所有课程都可以按要求完成;否则说明存在循环依赖,无法完成所有课程。
|
||
*/
|
||
public class CanFinish {
|
||
public boolean canFinish(int numCourses, int[][] prerequisites) {
|
||
// 构建图:graph.get(i) 存储课程 i 的后续课程
|
||
List<List<Integer>> graph = new ArrayList<>();
|
||
// 记录每个课程的入度(即需要先修课程的数量)
|
||
int[] indegree = new int[numCourses];
|
||
|
||
// 初始化图
|
||
for (int i = 0; i < numCourses; i++) {
|
||
graph.add(new ArrayList<>());
|
||
}
|
||
|
||
// 构建图和入度数组
|
||
// 每个先修对 [ai, bi] 表示:要修 ai 必须先修 bi
|
||
// 因此 bi 指向 ai,所以 indegree[ai]++
|
||
for (int[] pre : prerequisites) {
|
||
int course = pre[0];
|
||
int preCourse = pre[1];
|
||
graph.get(preCourse).add(course);
|
||
indegree[course]++;
|
||
}
|
||
|
||
// 使用队列存储所有入度为 0 的课程
|
||
Queue<Integer> queue = new LinkedList<>();
|
||
for (int i = 0; i < numCourses; i++) {
|
||
if (indegree[i] == 0) {
|
||
queue.offer(i);
|
||
}
|
||
}
|
||
|
||
// 用于统计可以修的课程数
|
||
int count = 0;
|
||
while (!queue.isEmpty()) {
|
||
int cur = queue.poll();
|
||
count++;
|
||
// 遍历当前课程的所有后续课程,将它们的入度减 1
|
||
for (int next : graph.get(cur)) {
|
||
indegree[next]--;
|
||
// 如果入度减到 0,说明该课程的先修都已完成
|
||
if (indegree[next] == 0) {
|
||
queue.offer(next);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果所有课程都能修完,则 count 应等于 numCourses
|
||
return count == numCourses;
|
||
}
|
||
}
|