1、题目描述
2、解题思路
把课程关系看成有向图,课程 A 指向课程 B,则 B 的入度加一。
入度作为课程能否在本学期开课的依据。
如果一个课程的入度为 0,说明现在没有该课程的先修课程,可以直接上了。否则不能直接上,必须等到先修课程上完才能上它。
算法逻辑:
1、针对给定的课程关系,统计课程的入度和课程的所有后继课程;
2、初始时,入度就为 0 的课程就可以在第一学期上;
3、每上完一学期,就把这学期上的课的后继课程的入度减一,然后统计减一后新的入度为 0 的课程,作为下学期的开课课程。
4、经过 3 的循环,当最后一个学期结束,应该所有的课程入度都不大于 0,如果还存在某个课程入度大于 0,说明存在环,返回 -1。否则直接返回 3 的循环次数。
3、解题代码
class Solution {
public int minimumSemesters(int N, int[][] relations) {
// 每个课程都有不止一个后置课程
Map<Integer, List<Integer>> courses = new HashMap<>();
// inDegree[i] 表示课程 i 的入度
int[] inDegree = new int[N + 1];
for (int[] relation : relations) {
int x = relation[0];
int y = relation[1];
inDegree[y]++; // 课程 y 入度加一
if (courses.containsKey(x)) {
courses.get(x).add(y);
} else {
List<Integer> list = new ArrayList<>();
list.add(y);
courses.put(x, list);
}
}
// 当前学期要学的课,即入度为 0 的课
Set<Integer> curCourse = new HashSet<>();
for (int i = 1; i < inDegree.length; i++) {
if (inDegree[i] == 0) {
curCourse.add(i);
}
}
int term = 0; // 学期数
// 开始上课
while (!curCourse.isEmpty()) {
// 新学期开始了
Set<Integer> nextCourse = new HashSet<>(); // 下学期要上的课
// 开始上课,上课的内容就是把入度为 0 的课程的后置课程的入度减一,然后把新的入度为 0 的课程作为下学期的要上的课
for (Integer cur : curCourse) {
if (courses.get(cur) != null) {
List<Integer> y = courses.get(cur); // 课程 cur 的所有后置课程
for (Integer id : y) {
// 课程 id 的入度减一
inDegree[id]--;
// 判断现在 id 是不是入度变为 0 了,如果是,则下学期要上它了
if (inDegree[id] == 0) {
nextCourse.add(id);
}
}
}
}
// 上完课了,学期数加一
term++;
curCourse = nextCourse;
}
// 现在能上的已经上完了,如果还有课程入度大于 0,说明有环
for (int i = 1; i < inDegree.length; i++) {
if (inDegree[i] > 0) {
return -1;
}
}
return term;
}
}