拓撲排序

拓撲排序
1、定義
  對一個有向無環圖(Directed Acyclic Graph簡稱DAG) G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊(u,v)∈E(G),則u在線性序列中出現在v之前。通常,這樣的線性序列稱爲滿足拓撲次序(Topological Order)的序列,簡稱拓撲序列。簡單的說,由某個集合上的一個偏序得到該集合上的一個全序,這個操作稱之爲拓撲排序。
  在AOV網中,若不存在迴路,則所有活動可排列成一個線性序列,使得每個活動的所有前驅活動都排在該活動的前面,我們把此序列叫做拓撲序列(Topological order),由AOV網構造拓撲序列的過程叫做拓撲排序(Topological sort)。AOV網的拓撲序列不是唯一的,滿足上述定義的任一線性序列都稱作它的拓撲序列。

2、拓撲排序的實現步驟
在有向圖中選一個沒有前驅的頂點並且輸出
從圖中刪除該頂點和所有以它爲尾的弧(白話就是:刪除所有和它有關的邊)
重複上述兩步,直至所有頂點輸出,或者當前圖中不存在無前驅的頂點爲止,後者代表我們的有向圖是有環的,因此,也可以通過拓撲排序來判斷一個圖是否有環。
3、拓撲排序示例手動實現
如果我們有如下的一個有向無環圖,我們需要對這個圖的頂點進行拓撲排序,過程如下:
在這裏插入圖片描述
首先,我們發現V6和v1是沒有前驅的,所以我們就隨機選去一個輸出,我們先輸出V6,刪除和V6有關的邊,得到如下圖結果:

在這裏插入圖片描述
然後,我們繼續尋找沒有前驅的頂點,發現V1沒有前驅,所以輸出V1,刪除和V1有關的邊,得到下圖的結果:

在這裏插入圖片描述

然後,我們又發現V4和V3都是沒有前驅的,那麼我們就隨機選取一個頂點輸出(具體看你實現的算法和圖存儲結構),我們輸出V4,得到如下圖結果:

在這裏插入圖片描述
然後,我們輸出沒有前驅的頂點V3,得到如下結果:

在這裏插入圖片描述
然後,我們分別輸出V5和V2,最後全部頂點輸出完成,該圖的一個拓撲序列爲:

v6–>v1—->v4—>v3—>v5—>v2

過程簡述:

從 DAG 圖中選擇一個 沒有前驅(即入度爲0)的頂點並輸出。
從圖中刪除該頂點和所有以它爲起點的有向邊。
重複 1 和 2 直到當前的 DAG 圖爲空或當前圖中不存在無前驅的頂點爲止。若當前圖中不存在無前驅的頂點說明有向圖中必存在環。
基本操作函數
topological_sort(n):對含有n個頂點的圖進行拓撲排序並輸出。

代碼模板(含詳細註釋)
這裏用http://acm.hdu.edu.cn/showproblem.php?pid=1285作爲模板題

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define V 510 //最大頂點數
int G[V][V];    //圖
int degree[V];  //記錄各頂點的入度
void topological_sort(int n)    //拓撲排序函數
{
    int i, j, k;
    for(i = 1; i <= n; i++){
        for(j = 1; j <= n; j++){
            if(degree[j] == 0){     //找到入度爲0的頂點
                printf("%d", j);    //輸出
                degree[j]--;        //將其入度減爲-1
                k = j;              //用k記錄此頂點
                break;
            }
        }
        for(j = 1; j <= n; j++){
            if(G[k][j] == 1){   //找到被此頂點打敗過的頂點
                G[k][j] = 0;    //標記
                degree[j]--;    //將找到的頂點的入度減一
            }
        }
        if(i != n)
            printf(" ");
        else
            printf("\n");
    }
}
int main(void)
{
    int n;  //隊伍的個數
    int m;  //每組數據後接的輸入行數
    while(scanf("%d%d", &n, &m) != EOF){
        memset(G, 0, sizeof(G));            //圖的初始化
        memset(degree, 0, sizeof(degree));  //頂點入度的初始化
        while(m--){
            int u, v;
            scanf("%d%d", &u, &v);  //u打敗了v
            if(G[u][v] == 0){
                /*去重
                  這裏要記錄的是v被多少人打敗過,
                  而不是被打敗了多少次
                */
                G[u][v] = 1;    //u打敗過v
                degree[v]++;    //頂點v的入度加一
            }
        }
        topological_sort(n);    //調用拓撲排序函數
    }
    return 0;
}

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