拓扑排序

Posted by Marlin on June 8, 2025

介绍

拓扑排序(Topological Sorting)是一种常用的排序算法,它是对有向无环图(DAG, Directed Acyclic Graph)的一种排序方法。

应用

拓扑排序在许多领域都有重要的应用。

拓扑排序可以用于: 检测有向环:如果一个有向图无法进行拓扑排序,说明该图中存在有向环。因此,拓扑排序可以用于检测有向图中是否存在环,这在许多图算法和问题中都有重要的应用。 最短路径和关键路径分析:在有向无环图中,拓扑排序可以与其他算法结合使用,来求解最短路径问题和关键路径问题。例如,在关键路径法(CPM)中,拓扑排序可以用于确定项目中的关键任务,从而帮助项目管理者合理安排资源,缩短项目工期。 图的遍历和搜索:拓扑排序可以作为图遍历和搜索算法的预处理步骤,例如在深度优先搜索(DFS)中,拓扑排序可以帮助确定搜索的顺序,提高搜索效率。

拓扑排序的顺序可能不只一种。

算法原理

  1. 在图中找到所有入度为0的点
  2. 把所有入度为0的点在图中删掉,重点是删除影响!继续找到入度为0的点并删掉影响
  3. 直到所有点都被删掉,依次删除的顺序就是正确的拓扑排序结果
  4. 如果能够删除所有的点(拓扑排序结果包含所有节点),说明有向图里没有环;否则说明有环

算法步骤

  1. 建立邻接表,记录每个结点的入度
  2. 初始化入度为0的队列
  3. 循环队列,每次取出一个入度为0的结点,将其放入拓扑排序结果,并将其所有邻接点的入度减1,如果入度减1后变为0,则将邻接点入队列
  4. 循环结束后,如果拓扑排序结果包含所有结点,则说明无环;否则说明有环

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
int main(){
    int n;
    cin >> n;

    vector<int> inDegree(n + 10, 0); // 记录每个结点的入度
    queue<int> q; // 队列记录入度为0的结点
    vector<int> topoSort; // 拓扑排序结果
    vector<int> g[n + 10]; // 邻接表
    
    for(int i = 1; i <= n; i++){ // 建图并记录入度
        int uu, vv;
        cin >> uu >> vv;
        g[uu].push_back(vv); // 建立邻接表
        inDegree[vv]++; // 记录每个结点的入度
    }
    for(int i = 1; i <= n; i++){ // 初始化入度为0的队列
        if(inDegree[i] == 0){
            q.push(i); // 入度为0的结点入队列
        }
    }
    while(!q.empty()){
        int u = q.front();
        q.pop();
        topoSort.push_back(u); // 拓扑排序结果
        for(int v : g[u]){
            inDegree[v]--; // 减少每个邻接点的入度
            if(inDegree[v] == 0){
                q.push(v); // 入度为0的邻接点入队列
            }
        }
    }
    
    if(topoSort.size() == n){ // 判断是否有环
        cout << "No" << endl; // 无环
    }else{
        cout << "Yes" << endl; // 有环
    }
    for(int u : topoSort){
        cout << u << " "; // 输出拓扑排序结果
    }
    return 0;
}