什么是拓扑排序?
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
拓扑排序算法具体步骤
1、 调用dfs_travel();
2、 在dfs_travel()每次调用dfs()的过程中,都记录了顶点s的完成时间,将顶点s按完成顺序保存在存放拓扑排序顺序的数组topoSort[]中。这样,该数组就存放了按先后顺序访问完成的所有顶点。
3、 最后拓扑排序得到的线性序列,即为topoSort[]的逆序。
拓扑排序解的唯一性
- 哈密顿路径
哈密顿路径是指一条能够对图中所有顶点正好访问一次的路径。当一个DAG中的任何两个顶点之间都存在可以确定的先后关系时,对该DAG进行拓扑排序的解是唯一的。这是因为它们形成了全序的关系,而对存在全序关系的结构进行线性化之后的结果必然是唯一的(比如对一批整数使用稳定的排序算法进行排序的结果必然就是唯一的)。需要注意的是,非DAG也是能够含有哈密顿路径的,为了利用拓扑排序来实现判断,所以这里讨论的主要是判断DAG中是否含有哈密顿路径的算法。
既然知道了哈密顿路径和拓扑排序的关系,我们如何快速检测一张图是否存在哈密顿路径呢?
根据前面的讨论,是否存在哈密顿路径的关键,就是确定顶点是否存在全序的关系,而全序的关键,就是任意一对顶点之间都是能够确定先后关系的。因此,我们能够设计一个算法,用来遍历顶点集中的每一对顶点,然后检查它们之间是否存在先后关系,如果所有的顶点对有先后关系,那么该顶点集就存在全序关系,即存在哈密顿路径。
但是很显然,这样的算法十分低效。对于大规模的顶点集,是无法应用这种解决方案的。通常一个低效的解决办法,十有八九是因为没有抓住现有问题的一些特征而导致的。因此我们回过头来再看看这个问题,有什么特征使我们没有利用的。还是举对整数进行排序的例子:
比如现在有3, 2, 1三个整数,我们要对它们进行排序,按照之前的思想,我们分别对(1,2),(2,3),(1,3)进行比较,这样需要三次比较,但是我们很清楚,1和3的那次比较实际上是多余的。我们为什么知道这次比较是多余的呢?我认为,是我们下意识的利用了整数比较满足传递性的这一规则。但是计算机是无法下意识的使用传递性的,因此只能通过其它的方式来告诉计算机,有一些比较是不必要的。所以,也就有了相对插入排序,选择排序更加高效的排序算法,比如归并排序,快速排序等,将n2的算法加速到了nlogn。或者是利用了问题的特点,采取了更加独特的解决方案,比如基数排序等。
现在我们没有利用到的就是全序关系中传递性这一规则。怎么利用它呢,最简单的想法往往就是最实用的,我们还是选择排序,排序后对每对相邻元素进行检测不就间接利用了传递性这一规则嘛?所以,我们先使用拓扑排序对顶点进行排序。排序后,对每对相邻顶点进行检测,看看是否存在先后关系,如果每对相邻顶点都存在着一致的先后关系。那么就可以确定存在哈密顿路径了,反之则不存在。
- 实现代码