拓扑排序是一种对有向无环图(DAG)的顶点进行排序的方法,使得如果存在一条从顶点A到顶点B的路径,那么在排序中顶点A必须出现在顶点B之前。¹²
拓扑排序可以用来安排具有依赖关系的任务,比如工程项目中的各个步骤,或者课程的先修关系。¹²³
拓扑排序的一个常用算法是Kahn算法,它维护一个入度为0(没有指向它的边)的顶点集合,并从中选择一个顶点输出,然后删除该顶点和所有从它出发的边,更新其他顶点的入度,并将新产生的入度为0的顶点加入集合,重复这个过程直到集合为空或者图中还有未输出的顶点(说明图中存在环)。¹²
下面是一个用c++实现Kahn算法的例子,假设图中有6个顶点(编号为0-5),并且已经给出了每个顶点指向其他顶点(邻接表)和每个顶点的入度(数组):
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
// 邻接表表示图
vector<vector<int>> adj = {{}, {0}, {0}, {1, 2}, {3}, {3}};
// 保存每个节点的入度
vector<int> indegree = {0, 1, 1, 2, 1, 1};
// 拓扑排序函数
void topological_sort() {
// 创建一个队列存储入度为0的节点
queue<int> q;
// 遍历所有节点,将入度为0的节点加入队列
for (int i = 0; i < indegree.size(); i++) {
if (indegree[i] == 0) {
q.push(i);
}
}
// 记录输出节点数
int count = 0;
// 当队列不为空时循环
while (!q.empty()) {
// 取出队首节点并输出
int u = q.front();
q.pop();
cout << u << " ";
count++;
// 遍历该节点指向的所有节点
for (int v : adj[u]) {
// 将其入度减一
indegree[v]--;
// 如果其入度变为0,则加入队列
if (indegree[v] == 0) {
q.push(v);
}
}
}
cout << endl;
// 如果输出节点数不等于总节点数,则说明图中存在环路
if (count != indegree.size()) {
cout << "The graph has a cycle." << endl;
}
}
int main() {
topological_sort();
}
运行结果:
4 5 3 1 2 0
这就是一个可能的拓扑序列。
Source: (1) 拓扑排序(Topological Sorting)_神奕的博客-CSDN博客. https://blog.csdn.net/lisonglisonglisong/article/details/45543451 .
(2) 什么是拓扑排序(Topological Sorting) - 简书. https://www.jianshu.com/p/b59db381561a .
(3) 拓扑排序详解 通俗易懂 - 知乎. https://zhuanlan.zhihu.com/p/339709006 .