1. 程式人生 > 實用技巧 >拓撲排序

拓撲排序

首先明白什麼是拓撲序列?

只有有向圖才會有拓撲序列,每一條邊的起點都在終點之前
例如:

但如果是這樣子的

有向圖中存在一個環 則它就不存在拓撲序列。

拓撲圖:有向無環圖一定存在拓撲序列,因此有向無環圖也叫做拓撲圖,所有入度為0的節點都可以作為起點,因此拓撲序列並不唯一。**

有向無環圖一定存在拓撲序列是因為,一定至少存在一個入度為0的點,因此可以逐個突破,生成拓撲序,而一個環上的每個點入度必不為0。

生成拓撲序列的步驟

queue <- 所有入度為0的點
while queue不為空
{
      t <- 取隊頭
      列舉t所有出邊 t-j
      刪掉t->j    j入度減一:d[j]--
      if(d[j] == 0) 說明節點j的入度為0 可以作為起點入隊 queue <- j
      所有點全部入隊 拓撲序列排序完成
}

輸出拓撲序(在佇列中)

而且這個題體現出了用陣列模擬queue的好處,用佇列陣列儲存的正好是拓撲序

程式碼:

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 100010;

int n, m;
int h[N], e[N], ne[N], idx;
int q[N], d[N];  //d用來存一個點的入度

bool torsort()
{
    int hh = 0, tt = 0;
    
    for(int i = 1; i <= n; i++) //把入度數為0的節點全部入隊
    {
        if(!d[i])
        {
            q[++tt] = i;
        }
    }

    while(hh <= tt)
    {
        int t = q[hh++];

        for(int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            d[j]--; //刪出邊
            if(d[j] == 0) q[++tt] = j; //入隊
        }
    }

    return tt == n; //所有點都入隊 則完成拓撲序
}

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

int main()
{
    memset(h, -1, sizeof h);

    scanf("%d%d", &n, &m);
    for(int i = 0; i < m; i++)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b);
        d[b]++;  //入度加1
    }

    if(torsort())
    {
        for(int i = 1; i <= n; i++) printf("%d ", q[i]);
        puts("");
    }
    else puts("-1");

    system("pause");
    return 0;
}