现在你总共有 numCourses 门课需要选,记为 0 到 numCourses - 1。给你一个数组 prerequisites ,其中 prerequisites[i] = [ai, bi] ,表示在选修课程 ai 前 必须 先选修 bi 。
例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示:[0,1] 。
返回你为了学完所有课程所安排的学习顺序。可能会有多个正确的顺序,你只要返回 任意一种 就可以了。如果不可能完成所有课程,返回 一个空数组 。

题解 拓扑排序(广度优先遍历)
Leetcode 207 课程表题解链接
本题与207的区别在于207判断能否学完所有的课程(是否有环),本题判断顺序
题解转载自liweiwei1419
BFS 的写法就叫「拓扑排序」,这里还用到了贪心算法的思想,贪的点是:当前让入度为 0 的那些结点入队;
「拓扑排序」的结果不唯一;
删除结点的操作,通过「入度数组」体现,这个技巧要掌握;
「拓扑排序」的一个附加效果是:能够顺带检测有向图中是否存在环,这个知识点非常重要,如果在面试的过程中遇到这个问题,要把这一点说出来。
拓扑排序实际上应用的是贪心算法,贪心算法简而言之:每一步最优,则全局最优。
具体到拓扑排序,每一次都从图中删除没有前驱的顶点,这里并不需要真正的做删除操作,我们可以设置一个入度数组,每一轮都输出入度为 0 的结点,并移除它、修改它指向的结点的入度(−1即可),依次得到的结点序列就是拓扑排序的结点序列。如果图中还有结点没有被移除,则说明“不能完成所有课程的学习”。
算法流程:
1、在开始排序前,扫描对应的存储空间(使用邻接表),将入度为 0 的结点放入队列。
2、只要队列非空,就从队首取出入度为 0 的结点,将这个结点输出到结果集中,并且将这个结点的所有邻接结点(它指向的结点)的入度减 1,在减 1 以后,如果这个被减 1 的结点的入度为 0 ,就继续入队。
3、当队列为空的时候,检查结果集中的顶点个数是否和课程数相等即可。(思考这里为什么要使用队列?如果不用队列,还可以怎么做,会比用队列的效果差还是更好?)
在代码具体实现的时候,除了保存入度为 0 的队列,我们还需要两个辅助的数据结构:
1、邻接表:通过结点的索引,我们能够得到这个结点的后继结点;
2、入度数组:通过结点的索引,我们能够得到指向这个结点的结点个数。
这个两个数据结构在遍历题目给出的邻边以后就可以很方便地得到。
class Solution {
public int[] findOrder(int numCourses, int[][] prerequisites) {
if (numCourses <= 0) {
return new int[0];
}
//存储每门课程的邻居节点
HashSet<Integer>[] adj = new HashSet[numCourses];
for (int i = 0; i < numCourses; i++) {
adj[i] = new HashSet<>();
}
//存储每门课程的入度值
int[] inDegree = new int[numCourses];
for (int[] p : prerequisites) {
adj[p[1]].add(p[0]);
inDegree[p[0]]++;
}
//存储入度值为零的节点
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < numCourses; i++) {
if (inDegree[i] == 0) {
queue.offer(i);
}
}
//结果集列表
int[] res = new int[numCourses];
// 当前结果集列表里的元素个数,正好可以作为下标
int count = 0;
while (!queue.isEmpty()) {
// 当前入度为 0 的结点
Integer head = queue.poll();
res[count] = head;
count++;
//当前节点的邻居节点
Set<Integer> successors = adj[head];
for (Integer nextCourse : successors) {
inDegree[nextCourse]--;
// 马上检测该结点的入度是否为 0,如果为 0,马上加入队列
if (inDegree[nextCourse] == 0) {
queue.offer(nextCourse);
}
}
}
// 如果结果集中的数量不等于结点的数量,就不能完成课程任务,这一点是拓扑排序的结论
if (count == numCourses) {
return res;
}
return new int[0];
}
}

本文介绍了如何使用拓扑排序算法解决LeetCode课程表问题,通过BFS实现贪心策略,找出课程学习的正确顺序,同时提及了算法背后的逻辑和检测环的特性。
386

被折叠的 条评论
为什么被折叠?



