210.Course Schedule II (M)
https://leetcode.com/problems/course-schedule-ii/
1.Description(Medium)
There are a total of n courses you have to take, labeled from0
ton - 1
.
Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair:[0,1]
Given the total number of courses and a list of prerequisite pairs, return the ordering of courses you should take to finish all courses.
There may be multiple correct orders, you just need to return one of them. If it is impossible to finish all courses, return an empty array.
Example
Given n =2
, prerequisites =[[1,0]]
Return[0,1]
Given n = 4, prerequisites =[1,0],[2,0],[3,1],[3,2]]
Return[0,1,2,3]
or[0,2,1,3]
2.Code
Version 1: BFS
这道题我们得找出要上的课程的顺序,即有向图的拓扑排序.
此题正是基于之前解法的基础上稍加修改,我们从queue中每取出一个数就将其存在结果中,最终若有向图中有环,则结果中元素的个数不等于总课程数,那我们将结果清空即可(注意设置一个数组存放结果还有一个index去存放进数组的位置)
Version 2: Topological Sort https://labuladong.github.io/algo/2/19/35/
先说一下拓扑排序(Topological Sorting)这个名词,网上搜出来的定义很数学,这里干脆用百度百科的一幅图来让你直观地感受下:
直观地说就是,让你把一幅图「拉平」,而且这个「拉平」的图里面,所有箭头方向都是一致的,比如上图所有箭头都是朝右的。
很显然,如果一幅有向图中存在环,是无法进行拓扑排序的,因为肯定做不到所有箭头方向一致;反过来,如果一幅图是「有向无环图」,那么一定可以进行拓扑排序。
但是我们这道题和拓扑排序有什么关系呢?
其实也不难看出来,如果把课程抽象成节点,课程之间的依赖关系抽象成有向边,那么这幅图的拓扑排序结果就是上课顺序。
首先,我们先判断一下题目输入的课程依赖是否成环,成环的话是无法进行拓扑排序的,所以我们可以复用上一道题的主函数:
那么关键问题来了,如何进行拓扑排序?是不是又要秀什么高大上的技巧了?
其实特别简单,将后序遍历的结果进行反转,就是拓扑排序的结果。
直接看解法代码吧,在上一题环检测的代码基础上添加了记录后序遍历结果的逻辑:
代码虽然看起来多,但是逻辑应该是很清楚的,只要图中无环,那么我们就调用 traverse
函数对图进行 DFS 遍历,记录后序遍历结果,最后把后序遍历结果反转,作为最终的答案。
那么为什么后序遍历的反转结果就是拓扑排序呢?
我这里也避免数学证明,用一个直观地例子来解释,我们就说二叉树,这是我们说过很多次的二叉树遍历框架:
二叉树的后序遍历是什么时候?遍历完左右子树之后才会执行后序遍历位置的代码。换句话说,当左右子树的节点都被装到结果列表里面了,根节点才会被装进去。
后序遍历的这一特点很重要,之所以拓扑排序的基础是后序遍历,是因为一个任务必须在等到所有的依赖任务都完成之后才能开始开始执行。
你把每个任务理解成二叉树里面的节点,这个任务所依赖的任务理解成子节点,那你是不是应该先把所有子节点处理完再处理父节点?这是不是就是后序遍历?
再说一说为什么还要把后序遍历结果反转,才是最终的拓扑排序结果。
我们说一个节点可以理解为一个任务,这个节点的子节点理解为这个任务的依赖,但你注意我们之前说的依赖关系的表示:如果做完 A
才能去做 B
,那么就有一条从 A
指向 B
的有向边,表示 B
依赖 A
。
那么,父节点依赖子节点,体现在二叉树里面应该是这样的:
是不是和我们正常的二叉树指针指向反过来了?所以正常的后序遍历结果应该进行反转,才是拓扑排序的结果。
以上,我简单解释了一下为什么「拓扑排序的结果就是反转之后的后序遍历结果」,当然,我的解释虽然比较直观,但并没有严格的数学证明,有兴趣的读者可以自己查一下。
总之,你记住拓扑排序就是后序遍历反转之后的结果,且拓扑排序只能针对有向无环图,进行拓扑排序之前要进行环检测,这些知识点已经足够了。
Last updated