圖論-拓撲排序詳解

拓撲排序(topsort)詳解

這篇隨筆就信息學奧林匹克競賽中圖論的一個知識點——拓撲排序進行講解。拓撲排序的內容比較基礎,只要求讀者學習過並瞭解信息學中圖的相關定義和一些專業名詞,可是拓撲排序的變形題目比較多,但願讀者在看完本隨筆後認真體會練習,掌握拓撲排序。算法

上課!

拓撲排序的定義

顧名思義,這是一種排序,確切地說,是一種圖上排序,在一張有向無環圖(註解:有向無環圖即不少參考書和題解中所說的DAG)上進行排序,把其中的全部節點排成一個序列,使得圖中的任意一對有邊相連的節點(u,v)u要出如今v前。數組

因此我再次強調,拓撲排序只能用在有向無環圖中!!學習

這樣的線性序列咱們稱之爲拓撲序。code

注意,拓撲序不惟一!這個地方不明白的請本身畫圖理解(或者參考下面的那棵樹)。blog

拓撲排序的實現原理

在講解圖的拓撲排序以前,咱們能夠用一棵樹來加深對拓撲排序的理解(由於樹是絕對沒有環)。排序

咱們隨意地定義一棵有向樹(以下圖),若是咱們想獲得它的拓撲序,那會很簡單,只須要先把根節點8號放進隊列中,而後再放8號的任意一個兒子節點,繼續此操做。直到節點全放進去爲止。隊列

咱們會發現,問放進去的是任意的一個子節點,因此咱們說拓撲序是不惟一的(在絕大多數狀況下,你要非跟我擡槓說假如只有一條鏈,我也沒辦法)。it

拓撲排序的實現

講完了實現原理,咱們來進行拓撲排序的代碼實現,根據上面的原理,咱們會發現,咱們要保證拓撲序列的正確性,只須要把圖中的入度爲0的節點先放進拓撲序,而後把這個點和它全部的出邊所有刪掉,這樣就還會出現一些入度爲0的點,咱們繼續重複以上操做。io

有細心的小夥伴會發現這個和算法中的寬搜很類似,沒錯,所謂寬搜和深搜,都是基於對樹與圖的深度/寬度優先遍歷而定義的,因此拓撲排序的實現其實就是藉助了寬搜的思想。模板

上模板:

void topsort()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(rudu[j]==0)
            {
                x=j;
                top[++cnt]=j;
                rudu[j]--;
                break;
            }
        }
        for(int j=head[x];j;j=nxt[j])
            rudu[j]--;
    }
}

以上代碼的top數組存拓撲序列,使用的是鏈式前向星存圖並遍歷。比較好理解,可是時間複雜度比較低。(因此僅供理解)

因此咱們用C++STL來實現拓撲排序,這樣會快不少。

模板:

void topsort()
{
    queue<int> q;
    for(int i=1;i<=n;i++)
        if(rudu[i]==0)
            q.push(i);
    while(q.empty())
    {
        int x=q.front();
        q.pop();
        top[++cnt]=x;
        for(int i=head[x];i;i=nxt[i])
        {
            int y=to[i];
            rudu[y]--;
            if(rudu[y]==0)
                q.push(y);
        }
    }
}

其實也很好理解啦...

注意,以上代碼是針對於已經保證圖是DAG的狀況下而出現的,假如咱們沒有題目中DAG的保證,就要額外地判這個圖是否是DAG,即不少題目中要求的「無解」狀況。

怎麼判斷呢?

學到這裏,我以爲你應該想到,若是最後獲得的拓撲序列的長度等於節點總數,那麼這個圖就是DAG,不然就不是。

因此咱們最後進行判斷。

(你也可使用STL中的vector容器)

代碼:

if(cnt==n)
    //DAG操做
else
    //非DAG操做

拓撲排序的用途及一些技巧

拓撲排序的用途是解決一些依賴關係的題,通常來說沒有圖論的基本要素(告訴你幾個點,一眼就看出來這是一道圖論題%%%),因此,我認爲作拓撲排序題的難點在於如何創建一個和題意相符的圖(建圖坑死爹)。因此美其名曰拓撲排序是圖論中最簡單的內容,其實它的相關題目都頗有思惟含量,因此強烈建議各位同窗多刷題多刷題

因爲拓撲排序不惟一,因此有些坑爹題目要求拓撲序列的一些內容,好比按字典序等等。

這時咱們把本來的隊列拓撲排序換成優先隊列拓撲排序。

注意,優先隊列不能提速,不要覺得找到了一份更好的模板,必定要讀題~~!!

除了定義方式有點怪異其餘的跟隊列同樣。

priority_queue<int,vector<int>,greater<int> >q;
//取隊首的時候須要變成q.top();

下課!!祝同窗們AK IOI!!

相關文章
相關標籤/搜索