[算法] 拓扑排序

定义

对一个有向无环图(Directed Acyclic Graph,DAG)G进行拓扑排序,使得2任意一对定点 u u v v ,若边 (uv)E(G) ( u , v ) ∈ E ( G ) ,则在线性序列中 u u 出现在 v v 之前。如下图所示的DAG:
《[算法] 拓扑排序》
一种可能的拓扑排序是:2->8->0->3->7->1->5->6->9->4->11->10->12。

分析

  • 从有向图中选择一个没有前驱(即入度为0)的定点并输出它;
  • 从图中删除该顶点,并且删除从该顶点出发的所有有向边;
  • 重复上述两步,直到剩余的图中所有节点都没有前驱为止。

拓扑排序得到的结果并不唯一

代码实现

假设有向无环图中有 n n 个节点,可以借助二维数组 graph[n][n] g r a p h [ n ] [ n ] (邻接矩阵)来表示该图, graph[i][j] g r a p h [ i ] [ j ] 值为1则表示有从 i i 节点到 j j 节点的有向边,用一维数组 indegree[n] i n d e g r e e [ n ] 表示该图所有节点的入度,将 graph g r a p h 中第 j j 列的所有2相加即得到 indegree[j] i n d e g r e e [ j ] (即第 j j 个节点的入度),借助队列先进先出的特点来缓存入度为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的节点;
  • 拓扑排序可以用来判断一个有向图中是否存在环,若拓扑排序后得到的结果节点数量少于原图中节点数量,则有向图中存在环;
  • 拓扑排序其实是给定了节点的一组偏序关系;
    原文作者:拓扑排序
    原文地址: https://blog.csdn.net/zkp_java/article/details/80699761
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞