圖的拓撲排序詳解與實現

W24-發表於2020-11-08

基本概念

有向無環圖(DAG)

如何用有向無環圖(DAG,Directed Acyclic Graph) 來表示偏序關係?

  • R R R 是有窮集合 X X X 上的偏序關係,對 X X X 中每個 v v v,用一個以 v v v 為標號的頂點表示,由此構成頂點集 V V V;對任意 ( u , v ) ∈ R , ( u ≠ v ) ( u , v )∈R,( u ≠ v ) (u,v)R,(u=v)嚴格偏序或反自反偏序關係),由對應兩個頂點建立一條有向邊,由此構成邊集 E E E, 則 G = ( V , E ) G =( V , E ) G=(V,E) 是有向無環圖。

拓撲排序

拓撲排序(Topological Sorting):是由某個集合上的一個偏序關係得到該集合上的一個全序的過程,所得到的線性序列稱為拓撲序列(Topological order)

在圖論中,拓撲排序是一個有向無環圖的所有頂點的線性序列,且該序列必須滿足下面兩個條件:

  • 每個頂點出現且只出現一次;
  • 若存在一條從頂點 A 到頂點 B 的路徑,那麼在序列中頂點 A 出現在頂點 B 的前面;

AOV 網

AOV 網就是一種有向無環圖。

一個較大的工程往往被劃分成許多子工程,在整個工程中,有些子工程(活動)必須在其它有關子工程完成之後才能開始,也就是說,一個子工程的開始是以它的所有前序子工程的結束為先決條件的,但有些子工程沒有先決條件,可以安排在任何時間開始。

為了形象地反映出整個工程中各個子工程(活動)之間的先後關係,可用一個有向圖來表示,用頂點表示活動,用弧表示活動之間的優先關係,稱這樣的有向圖為頂點表示活動的網,簡稱 AOV 網

  • AOV 網中的弧表示活動之間存在的某種制約關係。
  • 一個 AOV 網應該是一個有向無環圖,即不應該帶有迴路,因為若帶有迴路,則迴路上的所有活動都無法進行。

其實 AOV 網就體現了各個子工程之間的一種偏序關係。在 AOV 網中所有活動可排列成一個線性序列,使得每個活動的所有前驅活動都排在該活動的前面,我們把此序列叫做拓撲序列,由AOV網構造拓撲序列的過程叫做拓撲排序。

AOV網的拓撲序列不是唯一的,滿足上述定義的任一線性序列都稱作它的拓撲序列。

DAG 拓撲排序演算法

課程及課程間的先修關係是偏序關係,可以用 AOV 網(DAG)表示。

在這裡插入圖片描述

利用 AOV 網進行拓撲排序的基本思想:

  • (1) 從 AOV 網中選擇一個沒有前驅的頂點並且輸出它;
  • (2) 從 AOV 網中刪去該頂點和所有以該頂點為尾的弧;
  • (3) 重複上述兩步,直到全部頂點都被輸出,或AOV網中不存在沒有前驅的頂點;

廣搜優先搜尋實現拓撲排序

演算法過程——可以使用廣度優先搜尋演算法(使用佇列)

  • (1)建立入度為零的頂點佇列

  • (2)掃描頂點表,將入度為0的頂點入隊(初始化);

  • (3)while(佇列不空)

    • (3.1)輸出隊頭結點;
    • (3.2)記下輸出結點的數目;
    • (3.3)刪去與之關聯的出邊;
    • (3.4)若有入度為 0 的結點,入隊。
  • (4)若輸出結點個數小於 n n n,則輸出有環路;否則拓撲排序正常結束。

注意事項:

  • 若圖中還有未輸出的頂點,但已跳出迴圈處理,說明圖中還剩下一些頂點,它們的入度都大於 0,也就是都有直接前驅,這時網路中必存在有向環。

與廣度優先搜尋的區別:

  • 搜尋起點是入度為 0 的頂點;
  • 需判斷是否有環路,看最後輸出的頂點數與圖中頂點數是否相同;
  • 需刪除鄰接於 v 的邊(引入陣列 indegree[ ] 或在頂點表中增加一個屬性域 indegree

虛擬碼如下:

void Topologicalsort(AdjGraph G)
{ 
    QUEUE Q ; 
    count = 0 ;
    MAKENUlLL(Q) ;

    for(v=1; v<=G.n; ++v)
        if(indegree[v] ==0) 
            ENQUEUE(v, Q) ;
    while(!EMPTY(Q)) {
        v = FRONT(Q) ;
        DEQUEUE(Q) ;

        cout << v << " ";  // 廣度優先搜尋中,入佇列時訪問或出佇列時訪問是一樣的
        count ++ ;

        for(鄰接於 v 的每個頂點 w)
            if(!(--indegree[w])) 
                ENQUEUE(w,Q) ;
    }
    if(count < n) 
        cout << “圖中有環路” ;
}

深度優先搜尋非遞迴實現拓撲排序

也可以使用深度優先搜尋來獲取拓撲序列,遞迴或非遞迴實現均可。

在深度優先搜尋的非遞迴實現中,需要用到棧:

  • (1)建立入度為零的頂點棧;

  • (2)掃描頂點表,將入度為0的頂點棧;

  • (3)while(棧不空)

    • (3.1)輸出隊頭結點;
    • (3.2)記下輸出結點的數目;
    • (3.3)刪去與之關聯的出邊;
    • (3.4)若有入度為0的結點,入棧;
  • (4)若輸出結點個數小於 n n n,則輸出有環路;否則拓撲排序正常結束。

虛擬碼如下:

void Topologicalsort(AdjGraph G)
{ 
    STACK S ; 
    count = 0 ;
    MAKENUlLL(S) ;

    for(v=1; v<=G.n; ++v)
        if(indegree[v] == 0) 
            PUSH(v, S) ;
    while(!EMPTY(S)) {
        v = top(S) ;
        S.pop() ;

        cout << v << " ";  // 深度優先搜尋中,必須在出棧時訪問
        count ++ ;

        for(鄰接於 v 的每個頂點 w)
            if(!(--indegree[w])) 
                PUSH(w,S) ;
    }
    if(count < n) 
        cout << “圖中有環路” ;
}

深度優先搜尋遞迴實現拓撲排序

待補。

相關文章