前言
拓扑排序是有向无环图中的应用。所谓有向无环图,顾名思义,首先要求图是有向图,其次要求图中不能有回路,即无环。
使用有向无环图,可以来表示一个流程图,图中的每一条有向边用来表示子工程之间的次序(领先)关系。有时候,我们会关心工程的进行问题,假设子工程A领先于子工程B,这意味着要完成子工程B必须先完成子工程A,因此A必须在B之前进行,否则整个工程就无法顺利进行。
那么,如何得到整个工程的进行顺序呢?对有向无环图进行拓扑排序,将其转化为拓扑序列,就能解决这个问题。
拓扑排序算法的实现
要进行拓扑排序,思路很简单:
-
首先,在图中寻找一个入度为零(没有前驱)的顶点,将其输出。
没有前驱意味着要完成这项子工程不需要先完成其他子工程,因此可以先完成此工程。
-
从图中删除掉这个顶点和所有以它为尾的弧
删除掉这个顶点,意味着该项工程已经完成了,那么以它为前提的工程也就可以开始进行了。
-
重复1-2步,直到图中的全部顶点都输出完毕;或者当前的图中,已经不存在没有前驱的顶点了。假如是后一种情况,说明图中存在环。
-
如图所示,首先寻找到入度为0的顶点F,输出F,并且删除掉以它为尾的弧F-D、F-E,修改D、E的入度
-
寻找到入度为0的顶点A,输出A,并且删除掉以它为尾的弧A-B、A-C、A-D,修改B、C、D的入度
-
寻找到入度为0的顶点C,输出C,并且删除掉以它为尾的弧C-B、C-E,修改B、E的入度
-
寻找到入度为0的顶点B,输出B,无以它为尾的弧
-
寻找到入度为0的顶点D,输出D,并且删除掉以它为尾的弧D-E,修改E的入度
-
寻找到入度为0的顶点E,输出E,无以它为尾的弧
可以发现,入度为0的顶点不一定唯一,从中随机选择即可;这意味着拓扑排序的结果不一定是唯一的。
这个算法该怎么实现呢?
首先很显然,我们需要维护一个count数组,用于记录每个顶点当前的入度。当某个顶点入度为零时,意味着可以将其输出了。
从图中删除掉这个顶点,可以考虑用一个特殊值(-1)标记该顶点的访问情况,当count[i] == -1时,说明该顶点已经访问过了。而删除掉以它为尾的弧,意味着少了一条边到达本顶点的邻接顶点,也就是说将邻接顶点的入度减一即可。
如果这样设计,我们需要不断遍历count数组,寻找到count[i] == 0的顶点,并且在合适的时候(count数组内所有的值都等于-1时)退出这个循环,可以是可以,总归有些麻烦。
不如转换一下思路,使用栈(或者队列)的结构:首先计算所有顶点的初始入度,然后将count初值为零的顶点全部入栈,再将栈中的顶点依次出栈(出栈的同时修改其所有邻接顶点的入度,若为零则邻接顶点也入栈)。由于有n个顶点,因此出栈的操作应该循环n次;若在少于n次时栈已经空了,说明图中存在回路。
//拓扑排序
void TopologicalSort2(GraphLnk *g){
int n = g->NumVertices;
//创建辅助数组
int *count = (int *)malloc(sizeof(int)*n);
assert(count != NULL);
//初始化辅助数组
for (int i = 0; i < n; i ++) {
count[i] = 0;
}
//统计每个顶点的初始入度
Edge