拓扑排序与Kahn算法
定义
拓扑排序(Topological sorting)要解决的问题是如何给一个有向无环图的所有节点排序。
我们可以拿大学每学期排课的例子来描述这个过程,比如学习大学课程中有:「程序设计」,「算法语言」,「高等数学」,「离散数学」,「编译技术」,「普通物理」,「数据结构」,「数据库系统」等。按照例子中的排课,当我们想要学习「数据结构」的时候,就必须先学会「离散数学」,学习完这门课后就获得了学习「编译技术」的前置条件。当然,「编译技术」还有一个更加前的课程「算法语言」。这些课程就相当于几个顶点 , 顶点之间的有向边 就相当于学习课程的顺序。教务处安排这些课程,使得在逻辑关系符合的情况下排出课表,就是拓扑排序的过程。

但是如果某一天排课的老师打瞌睡了,说想要学习 数据结构,还得先学 操作系统,而 操作系统 的前置课程又是 数据结构,那么到底应该先学哪一个(不考虑同时学习的情况)?在这里,数据结构 和 操作系统 间就出现了一个环,显然同学们现在没办法弄清楚自己需要先学什么了,也就没办法进行拓扑排序了。因为如果有向图中存在环路,那么我们就没办法进行拓扑排序。
因此我们可以说 在一个DAG(有向无环图)中,我们将图中的顶点以线性方式进行排序,使得对于任何的顶点 到 的有向边 , 都可以有 在 的前面。
还有给定一个 DAG,如果从 到 有边,则认为 依赖于 。如果 到 有路径( 可达 ),则称 间接依赖于 。
拓扑排序的目标是将所有节点排序,使得排在前面的节点不能依赖于排在后面的节点。
Kahn 算法
构造拓扑序列步骤
- 从图中选择一个入度为零的点。
- 输出该顶点,从图中删除此顶点及其所有的出边。
重复上面两步,直到所有顶点都输出,拓扑排序完成,或者图中不存在入度为零的点,此时说明图是有环图,拓扑排序无法完成,陷入死锁。
过程
初始状态下,集合 装着所有入度为 的点, 是一个空列表。
每次从 中取出一个点 (可以随便取)放入 , 然后将 的所有边 删除。对于边 ,若将该边删除后点 的入度变为 ,则将 放入 中。
不断重复以上过程,直到集合 为空。检查图中是否存在任何边,如果有,那么这个图一定有环路,否则返回 , 中顶点的顺序就是构造拓扑序列的结果。
代码的核心是维持一个入度为 0 的顶点的集合。
可以参考该图

对其排序的结果就是:2 -> 8 -> 0 -> 3 -> 7 -> 1 -> 5 -> 6 -> 9 -> 4 -> 11 -> 10 -> 12
时间复杂度
假设这个图 在初始化入度为 的集合 的时候就需要遍历整个图,并检查每一条边,因而有 的复杂度。然后对该集合进行操作,显然也是需要 的时间复杂度。
因而总的时间复杂度就有