Algorithm/src/main/java/graph/CanFinish.java

84 lines
3.3 KiB
Java
Raw Normal View History

2025-03-25 15:57:54 +08:00
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;
}
}