1. 程式人生 > >資料結構與演算法21-圖的拓撲排序

資料結構與演算法21-圖的拓撲排序

拓撲排序

學了兩個有環的圖應用,現在我們來談談無環的圖應用。無環,即是圖中沒有迴路的意思。

拓撲排序介紹

在一個表示工作的有向圖中,用頂點表示活動,用弧表示活動之間的優先關係,這樣有向圖為頂點表示活動的網,我們稱為AOV網(Activity    On   Vertex Nextwork)

AOV網的弧表示活動之間存在的某種制約關係。比如演職人員確定了,場地也聯絡好啦,才可以開始進場拍攝。另外就是AOV網中不能存在迴路。

設G=(V,E)是一個具有n個頂點的有向圖,V中的頂點序列v1,v2,…,vn滿足若從頂點vi到vj有一條路徑,則有頂點序列中頂點vi必在頂點vj之前。則我們稱這樣的頂點序列為一個拓撲序列。

 

 

如上圖的AOV網的拓撲序列不止一條。序列v0 v1  v2  v3  v4  v5  v6  v7  v8  v9  v10 v11v12 v13  v14  v15  v16

而v0   v1  v4 v3  v2  v7  v6  v5  v8  v10  v9 v12 v11 v14 v13  v15  v16。也是一條拓撲序列。

所謂拓撲排序,其實就是對一個有向圖構造拓撲序列的過程。構造時會有兩個結果,如果此網的全部頂點都被輸出,則說明它是不存在環的AOV網;如果輸出頂點數少了,哪怕是少了一個,也說明這個網存在環(迴路),不是AOV網。

一個不存在迴路的AOV網,我們可以將它應用在各種各樣的工程或專案的流程圖中,滿足各種應用場景的需要,所以實現拓撲排序的演算法就很有價值。

拓撲排序演算法

對AOV網進行拓撲排序的基本思路是:從AOV網中選擇一個入度為0頂點輸出,然後刪去此頂點,並刪除以此頂點為尾的弧,繼續重複此步驟,直至輸出全部頂點或者AOV網中不存在入度為0的頂點為止

首先我們要確定一下這個圖需要使用的資料結構。前面 求最小生成樹和最短路徑時,我們用的都是鄰接矩陣,但由於拓撲排序的過程中,需要刪除頂點,顯然用鄰接表會更加方便。因此我們需要為AOV網建立一個鄰接表。考慮到演算法過程中始終要查詢入度為0的頂點,我們在原來頂點表結構中,增加一個入度域in。如圖

 

根據下圖,我們可以得到鄰接表的資料結構

 

 

/*拓撲排序,若GL無迴路,則輸出拓撲排序序列並返回OK,若有迴路返回ERROR*/

Status  TopologicalSort(GraphAdjList   GL)

{

     EdgeNode   *e;

     int   i,k,gettop;

     int   top=0;

     int   count=0;

     int    *stack;

     stack=(int  *)malloc(GL-numVertexes*sizeof(int));

     for(i=0;i<GL->numVertexes;i++)

     {

         if(GL->adjList[i].in==0)

               stack[++top]=i;

      }

      while(top!=0)

      {

            gettop = stack[top--]; //出棧

            printf(“%d->”,GL->adjList[gettop].data);  //列印頂點

            count++

            for(e=GL->adjList[gettop].firstedge;e;e=e->next)

           {

                     //對此頂點的弧表遍歷

                    k=e->adjvex;

                    if(!(--GL->adjList[k]->adjList[k].in))//將k號頂點鄰接點的入度減1

                               statck[++top]=k;

            }

       }

       if(count<GL->numVertexes)

              return   ERROR;

       else

              return   OK;

}

模擬一下:

1.    程式開始執行,第3~7行都是變數的定義,其中stack是一個棧,用來儲存整型的數字

2.    第8~10行,作了一個迴圈判斷,把入度為0的頂點下標都入棧,從圖的可知,此時stack應該為{0,1,3},即v0、v1、v3的頂點入度為0

 

3.    第12~23行,while迴圈,當棧中有資料元素時,始終迴圈。

4.    第14~16行,v3出棧得到gettop=3。並列印此頂點,然後count加1。

5.    第17~22行,迴圈其實是對v3頂點對應的弧連結串列進行遍歷,即下圖灰色部分,找到v3連線的兩個頂點v2和v13,並將它們的入度減少一位,此時v2和v13的in值為1。它的目的是為了將v3頂點上的弧刪除

 

6.    再次迴圈,第12~23行。此時處理的是頂點v1。經過出棧、列印count=2後,我們對v1到v2、v4、v8的弧進行了遍歷。並同樣減少了它們的入度數,此時v2入度為0,於是由第20~21行知,v2入棧,如圖,試想,如果沒有在頂點表中加入in這個入度資料域,20行的判斷就必須要是迴圈,,這顯然是消耗時間的,我們利用空間換取了時間

7.    接下來,就是同樣的處理方式了v2,v6,v0,v4,v5,v8的列印刪除過程,

8.    最終拓撲排序列印的結果3->1->2->6->0->4->5->8->7->12->9->10->13->11。當然這結果並不是唯一的一種拓撲排序方案。