拓撲排序

算法分析

拓撲排序:把事情看成圖的點,把先後關係看成有向邊,問題轉化爲在圖中求一個有先後關係的排序,就是拓撲排序。拓撲排序用BFS和DFS均可實現。

如何排序?

拓撲排序需要根據點的入度和出度,一個點的入度和出度體現了這個點的先後關係。如果一個點的入度等於0,說明它是起點,是排在最前面的。如果它的出度等於0,說明排在最後面。

因爲優先級相同的數的存在,拓撲排序可能不止一種結果。

例如:1 是最高優先級 (2 , 3)是同一優先級,5是最低優先級,則拓撲序可能是1、2、3、5或1、3、2、5;

算法步驟:
① 找到所有入度爲0的點,入隊,作爲起點,誰先誰後沒有關係,如果找不到入度爲0的點,說明不是有向無環圖(DAG),不存在拓撲排序。

② 彈出隊首a, a的所有鄰居點的入度減一,把入度減爲0的鄰居點b入隊,沒有減爲0的點不能放進隊列。

③ 繼續上述操作直至隊列爲空。

拓撲排序無解的判斷:如果隊列已空,但還有點未進入隊列,那麼這些點的入度都不是0,說明不是有向無環圖,不存在拓撲排序。

有向圖的拓撲排序

BFS手寫隊列實現。

n個點m條邊的有向圖,點的編號1~n,可能存在自環和重邊,若一個由圖中所有點構成的序列A滿足:對於圖中的每條邊(x, y),x在A中都出現在y之前,則稱A是該圖的一個拓撲序列。
如果存在拓撲序列輸出拓撲序列,否則輸出-1;

數據範圍

1≤n,m≤105

輸入樣例:

3 3
1 2
2 3
1 3

輸出樣例:

1 2 3
#include<iostream>
#include<cstring>
using namespace std;
const int N = 100010;
int h[N], e[N], ne[N], idx;
int n, m;
int q[N], d[N];
void add(int a, int b)
{//由數據範圍知這是一個稀疏圖,用鄰接表來寫
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
bool topsort()
{
    int hh = 0, tt = -1;
    for(int i = 1; i <= n; i ++ )
    {
        if(d[i] == 0)//入度邊爲0
            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] -- ;//入度邊數- 1
            if(d[j] == 0) q[++ tt] = j;//入度邊減爲0,入隊j
        }
    }
    return tt == n - 1;//共進了n個點
    //此步用來判斷是否無解,如果tt != n - 1說明某些點不在隊列中,表示無解。
}
int main()
{
    cin >> n >> m;

    memset(h, -1, sizeof h);

    for(int i = 0; i < m; i ++ )//
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
        d[b] ++ ;//入度邊數
    }
    if(topsort())//存在拓撲序列
    {
        for(int i = 0; i < n; i ++ ) cout << q[i] << " ";
    }
    else cout << "-1" << endl;
}

這是通過手寫隊列來寫,當然也可以使用STL隊列或優先隊列來寫。

複雜度分析:
① 在初始化時,查找入度爲0的點,需要檢查每個邊,複雜度O(m)
② 在隊列操作中,每個點進出隊列一次,需要檢查它直接連接的所有鄰居,複雜度O(n + m)
總複雜度爲O(n + m )

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章