定义
对一个有向无环图(Directed Acyclic Graph,DAG)G进行拓扑排序,使得2任意一对定点
u
u
、,若边
(u,v)∈E(G)
(
u
,
v
)
∈
E
(
G
)
,则在线性序列中
u
u
出现在之前。如下图所示的DAG:
一种可能的拓扑排序是:2->8->0->3->7->1->5->6->9->4->11->10->12。
分析
- 从有向图中选择一个没有前驱(即入度为0)的定点并输出它;
- 从图中删除该顶点,并且删除从该顶点出发的所有有向边;
- 重复上述两步,直到剩余的图中所有节点都没有前驱为止。
拓扑排序得到的结果并不唯一
代码实现
假设有向无环图中有 n n 个节点,可以借助二维数组(邻接矩阵)来表示该图, graph[i][j] g r a p h [ i ] [ j ] 值为1则表示有从 i i 节点到节点的有向边,用一维数组 indegree[n] i n d e g r e e [ n ] 表示该图所有节点的入度,将 graph g r a p h 中第 j j 列的所有2相加即得到(即第 j j <script type="math/tex" id="MathJax-Element-868">j</script>个节点的入度),借助队列先进先出的特点来缓存入度为0的节点。代码实现如TopologicalSort.hpp所示:
#ifndef TopologicalSort_hpp
#define TopologicalSort_hpp
#include <queue>
#include <stdio.h>
void topologicalSort(int[][] graph, int nodeCnt, int result) {
int[] indegree = new int[nodeCnt]; // 用于保存各节点入度
queue<int> q; // 用来缓存入度为0的节点
int cur; // 当前从队列头部取出的节点
int cnt = 0;
int i;
// 计算各节点入度
for (i = 0; i < nodeCnt; i++) { // 第i列
for (int j = 0; j < nodeCnt; j++) { // 第j行
indegree[i] += graph[j][i];
}
}
// 缓存初始入度为0的节点
for (i = 0; i < nodeCnt; i++) {
if (indegree[i] == 0) {
q.psuh(i);
}
}
while (!q.empty()) {
cur = q.front();
q.pop();
result[cnt++] = cur; // 保存当前元素到结果中
for (i = 0; i < nodeCnt; i++) {
if (graph[cur][i] != 0) {
// 删除从cur出发的所有边,即相邻节点的入度减1
if (--indegree[i] == 0) {
q.push[i];
}
}
}
}
}
#endif /* TopologicalSort_hpp */
总结
- 拓扑排序的本质是不断输出入度为0的节点;
- 拓扑排序可以用来判断一个有向图中是否存在环,若拓扑排序后得到的结果节点数量少于原图中节点数量,则有向图中存在环;
- 拓扑排序其实是给定了节点的一组偏序关系;