拓撲排序
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;
}