拓撲排序(C語言實現)
拓撲排序可以將一個有向無環圖轉換為一個線性序列。它也是判定一個有向圖是否是無環的方法之一。如何進行拓撲排序,方法如下:
(1)從有向圖中選取一個沒有前驅(入度為0)的頂點,並輸出之; (2)從有向圖中刪去此頂點以及所有以它為尾的弧(弧頭頂點的入度減1); 重複上述兩步,直至圖空,或者圖不空但找不到無前驅的頂點為止。 下圖:圖a為一個有向圖,入度為0的點有兩個,任選一個輸出,這裡輸出0,以0為弧尾的頂點入度減一,得圖b。繼續輸出入度為0的點,在此是1,輸出1之後,以1為弧尾的頂點入度減一,得圖c,以此類推,直到圖空為止。
這個理解起來是比較容易的,現在關鍵的是實現,如何得到入度為0的點?為避免每次都要搜尋入度為零的頂點,在演算法中
在拓撲排序演算法中,需要設定一個包含n個元素的一維整形陣列,假定用d表示,用它來儲存這個有向無環圖中每個頂點的入度值。對於上圖,得到陣列d的初始之為:
0 | 0 | 2 | 2 | 1 | 3 |
例如,根據上圖,建立鄰接表,如圖:
,建立入度為0的初始棧的過程如下:
(1)開始置鏈棧為空,即給鏈棧指標top賦初值為-1 top=-1;
(2)將入度為0的元素d[0]進棧,即: d[0]=top;top=0 /*因為d[0]存放下一個入度為0的頂點下標,在此,由於它是第一個入度為0的頂點(沒有下一個入度為0的頂點),因此d[0]的值為-1,top=0*/
(3)將入度為0的元素d[1]進棧,此時,d[1]裡應該保留下一個入度為0的頂點下標,而此時,下一個入度為0的頂點下標肯定是原top所指向的值,即:
d[1]=top;top=1;
(4)因d[2]至d[5]的值均不為0,所以它們均不進棧。至此,初始棧建立完畢,陣列d(多用途哦,即保留了頂點入度不為0的入度值,也作為鏈棧使用)如下圖所示:
現在開始迴圈執行拓撲演算法中的第一步"選擇一個入度為0的頂點並輸出之",利用輸出棧頂指標top所代表的頂點序號來實現,所以,輸出頂點1(因為top=1).頂點1輸出後,修改以頂點1為弧尾的頂點的入度,此例中,頂點4的入度為0,so,d[4]元素入棧,d[4]=0(d[4]=top;top=4,如果理解不了,請模擬一下鏈棧的出棧和入棧)。
現在頂點4輸出,以此為弧尾的頂點入度減一,得圖入下:
現在輸出頂點0,以此為弧尾的頂點入度減一,此時,頂點2的入度為0,因此入棧。d[2]=-1(d[2]=top;top=2),如圖
以此類推,直到top的值為-1,表示棧空,演算法執行結束。如果得到的頂點數是n(圖的頂點數),則表明該圖是有向無環圖,否則不是。
具體c語言實現:
#include <stdio.h>#include <stdlib.h>
void TopoSort(adjlist GL,int n)
{
int i,j,k,top,m=0; /*m用來統計拓撲序列中的頂點數*/
struct edgenode *p; /*單鏈表*/
int *d=(int *)malloc(n*sizeof(int));/*定義儲存圖中每個頂點入度的一維整形陣列d*/
for(i=0;i<n;i++)
d[i]=0; /*初始化陣列*/
for(i=0;i<n;i++) /*利用陣列d中的對應元素統計出圖中每個頂點的入度*/
{
p=GL[i];
while(p!=NULL)
{
j=p->adjvex;
d[j]++;
p=p->next;
}
}
top=-1; /*初始化用於連結入度為0的元素的棧的棧頂指標為-1*/
for(i=0;i<n;i++) /*建立初始化棧*/
if(d[i]==0)
{
d[i]=top;
top=i;
}
while(top!=-1) /*每迴圈一次刪除一個頂點及所有以它為弧尾的頂點入度減一*/
{
j=top /*j的值為一個入度為0的頂點序號*/
top=d[top]; /*得到下一個入度為0的頂點下標*/
printf("%d",j); /*輸出一個頂點*/
m++; /*輸出的頂點個數加1*/
p=GL[j]; /*p指向vj頂點鄰接表的第一個節點,目的是開始把以它為弧尾的頂點入度減一*/
while(p!=NULL)
{
k=p->adjvex; /*vk是vj的一個鄰接點*/
d[k]--; /*vk入度減一*/
if(d[k]==0) /*把入度為0的元素進棧,對應著圖看更容易明白*/
{
d[k]=top;
top=k;
}
p=p->next;
}
}
printf("\n");
if(m<n)
printf("有迴路");
free(d); /*刪除動態分配的陣列d*/
}
時間複雜度O(n+e)。