一、题目描述
你这个学期必须要学习numCourses门课程,记录为0到numCourses-1。在选修某些课程之前先修课程。例如,要学习课程0,必须先学习课程1,我们用一个匹配表示[0,1]。
输入:2,[[1,0]]
输出:true
解释:总共有2个课程,为了学习课程1,必须先学习课程0,所以这是可能的。
二、解答
课程 0 需要先修课程 1
课程 1 需要先修课程 2
课程 2 需要先修课程 3
- 需要一个数组inDegree,表示入度数据
- int[][] graph 表示课程表的图
1、构建图和计算入度
- 初始化每个课程的入度为0:inDegree = [0,0,0,0]
- 构建图,表示课程之间的依赖关系
课程 1 的列表添加课程 0
课程 2 的列表添加课程 1
课程 3 的列表添加课程 2
图表示为列表: graph = [[],[0],[1],[2]]
graph的索引代表课程编号,而每个索引对应的列表包含了依赖这个课程的后续课程。下标可以理解为学习课程的起点,数组内容就是后续要修的课程。
具体到这个例子中的graph = [[], [0], [1], [2]]
(1)graph[0] 是一个空列表,表示没有任何课程依赖课程0.
(2)graph[1] 是包含[0],表示课程1是课程0的先修课程,也就是说,你必须先完成课程1才能学习课程0
(3)graph[2]包含[1],表示课程2是课程1的先修课程,同样地,必须要完成课程2才能学习课程1。
(4)graph[3]包含[2],表示课程3是课程2的先修课程,你必须完成课程3才能学习课程2。
这个表直观展示了每个课程对哪些后续课程的先修条件。当我们在拓扑排序过程中选择一个课程时候,我们可以快速查找依赖它的课程,并对它们的入度进行更新。
例如,当我们从队列中取出课程3时候,我们查看graph[3],我们得知课程2依赖课程3,所以我们将课程2的入度减去1,如果课程2的入度为0,那么我将课程2加入队列,因为没有其他课程阻碍它了,它现在可以学习了。这个过程会一直重复,一直到所有课程的入度都被处理完毕。如果最终所有的课程入度减少到了0,那么我们就可以完成所有课程的学习。
- 更新每个课程的入度
课程 0 的入度为 1
课程 1 的入度为 1
课程 2 的入度为 1
课程 3 的入度为 0
更新后的入度: inDegree = [1, 1, 1, 0]。
2、执行BFS
(1)将所有的入度为0的课程加入队列:queue = [3]。
(2)初始化已完成的课程数量: count = 0。
3、开始排序
(1)从队列中取出课程3,将count加1,现在count加1,现在count = 1。
(2)检查课程3的邻接课程(依赖课程3的课程),这里是课程2。
(3)将课程2的入度减1,变为0,然后将课程2加入队列:queue = [2]。
4、继续BFS
(1)从队列中取出课程2,将count加1,现在count = 2。
(2)检查课程2的邻接课程,这里是课程1。
(3)将课程1的入度减1,变为0,然后将课程1加入队列:queue = [1]。
5、重复上述步骤
(1)从队列中取出课程1,将count加1,现在count = 3.
(2)检查课程1的邻接课程,这里是课程0。
(3)将课程9的入度减1,变为0,然后将课程0加入队列:queue = [0]。
6、最后一步
(1)从队列中取出课程0,将count加1,现在count = 4。
(2)课程0没有邻接课程,队列变为空。
7、检查结果
(1)最后 count = 4,等于课程总数 numCourses = 4。
(2)由于我们能够完成所有课程的学习,所以算法返回 true。
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
int[] inDegree = new int[numCourses];
Map<Integer, List<Integer>> graph = new HashMap<>();
// 构建图并计算入度
for (int[] prerequisite : prerequisites) {
int course = prerequisite[0];
int preCourse = prerequisite[1];
inDegree[course]++;
if (!graph.containsKey(preCourse)) {
graph.put(preCourse, new ArrayList<>());
}
graph.get(preCourse).add(course);
}
// 将所有入度为 0 的课程加入队列
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < numCourses; i++) {
if (inDegree[i] == 0) {
queue.add(i);
}
}
int count = 0;
// BFS
while (!queue.isEmpty()) {
int preCourse = queue.poll();
count++;
// 获取所有依赖 preCourse 的课程并更新它们的入度
if (graph.containsKey(preCourse)) {
for (int course : graph.get(preCourse)) {
inDegree[course]--;
if (inDegree[course] == 0) {
queue.add(course);
}
}
}
}
return count == numCourses;
}
}