Kahn算法拓扑排序

拓扑排序

1. 概念及规则

  • 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点uv,若边∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。

  • 规则:

    1. 图中每个顶点只出现一次。

    2. AB前面,则不存在BA前面的路径。(否则就会形成环)

    3. 顶点的顺序是

      保证所有指向它的下个节点在被指节点前面

      • 例如A—>B—>C那么A一定在B前面,B一定在C前面。
      • 这个核心规则下只要满足即可,所以拓扑排序序列不一定唯一

2. 算法及实现

  1. Kahn算法

    • 实现步骤:

      1. 维护一个入度为0的顶点的集合(栈、队列、优先队列皆可)
      2. 每次从该集合中取出一个顶点u(栈顶)
      3. 遍历u的邻接点v,v的入度rd[v]--,如果rd[v]==0,v进栈
      4. 队列为空,出栈数小于n,存在环
    • 时间复杂度:由于每个点入栈出栈一次,每条边扫描一次,时间复杂度为O(n + e)

    • 图示:

      1. 删除12以及对应边

        img

      2. 删除23以及对应边

        img

      3. 删除3或者4以及对应边

        img

      4. 重复以上规则步骤

        img

#include <bits/stdc++.h>
using namespace std;
csont int maxn = 10005;
struct edge{
    int to, nx;
}e[maxn];
int n, m, len, head[maxn], rd[maxn], a[maxn];
void insert(int x, int y){//插边
    e[++len].to = y;
    e[len].nx = head[x];
    head[x] = len;
}
void kahn(){
    stact<int> stk;//维护入度为0的点
    for(int i=1; i<n; i++) if(!rd[i]) stk.push(i);//如果入度为0,进栈
    int cnt = 0;//记录出栈次数
    while(!stk.empty()){
        int u = stk.top();//获取栈顶
        stk.pop();
        a[++cnt] = u;//记录拓扑序
        for(int i=head[u]; i; i=e[i].nx){//遍历邻接点
            int v = e[i].to;
            rd[v] --;//删边
            if(!rd[v]) stk.push(v);//入度为0,进栈
        }
    }
    if(cnt < n){printf("Cycle"); return;}//出栈次数小于n,有环
    for(int i=1; i<=cnt; i++) printf("%d ", a[i]);
}
int main(){
    scanf("%d%d", &n, &m);
    for(int i=1; i<=m; i++){
        int x, y; scanf("%d%d", &x, &y);
        insert(x, y); rd[y] ++;
    }
    kahn();
    return 0;
}
原文地址:https://www.cnblogs.com/hzoi-poozhai/p/12775596.html