1. 程式人生 > >拓撲排序(Topological Sort)

拓撲排序(Topological Sort)

0)拓撲排序

拓撲排序是對有向無圈圖的頂點的一種排序,這個排序的結果是如果存在一條vi到vj的路徑,那麼排序中vi在vj的前面。

下圖是一個有向無圈圖的例子:


在這個有向無圈圖中,1,6,5,7,4,2,3;1,6,5,7,2,4,3;這兩組都是拓撲排序,我們可以看到這兩種排序都滿足拓撲排序的要求,比如說1-4的路徑,可知1,7,4;1,6,5,4;1,6,7,4;1,6,5,7,4;這些路徑的點都按照拓撲排序的要求排列。

1) 簡單的拓撲排序演算法

下面我們介紹一個簡單的拓撲排序演算法:

a)先找到一個沒有輸入邊的點,輸出這個點,然後去掉與這個點連線的所有邊。

b)重複上面的步驟知道輸出所有的點。


這裡為了程式設計方便,我們要先定義一個叫做indegree的概念:

indegree: 頂點v包含的邊(u,v)的個數。

注意是有向圖的v包含的邊(u,v)的個數。因此對於上圖的點1,它的indegree=0,因為進入點1的邊為0,點1的三條邊全是指向外的。

所以通過引入indegree概念,上面的簡單演算法就可以用下面的方式表示

a) 查詢indegree為0的點p

b) 對所有與p鄰接的點的indegree = indegree -1;

c) 查詢indegree為0的點(p除外),然後迴圈過程

下面是簡單的拓撲排序演算法的一段虛擬碼

void TopSort(Graph G)
{
	int Num;
	Vertex V,W;
	for(Num=0;Num<NumVertex;Num++)
	{
		V = FindNewVertexOfDegreeZero();
		if(V==NotVertex)
		{
			Error("Graph has a cycle!");
			break;
		}
		Output[V] = Num;
		for each W adjacent to V
			Indegree[W]--;
	}
}
2) 拓撲排序的改進演算法
上面的簡單演算法還是很簡單的,也很好理解,那麼為什麼要改進上面的演算法呢?這個主要是因為上面演算法的時間複雜度為O(|V|^2)。

首先FindNewVertexOfDegreeZero()函式因為要找到indegree=0的點,所以需要遍歷所有的點,因此時間複雜度為O(|V|)。而這個函式需要重複|V|次,因此上面的演算法的時間複雜度為O(|V|^2)。因此我們需要做一些改進,來降低執行時間。

可以看到,這裡可能能夠進行改進的就是FindNewVertexOfDegreeZero()函式。當我們刪除一個indegree=0的點後,只有與這個鄰接的點的indegree才會減一,其他的點的indegree值不變,因此當我們需要在一次FindNewVertexOfDegreeZero時,不需要遍歷所有的點,只需要遍歷部分相關連的點就可以。

為了實現上面的思想,我們把indegree=0的點放到一個box中,因此FindNewVertexOfDegreeZero()函式只需要在這個box中尋找就好了。當一個點的indegree=0時,我們就把這個點輸入到box中。

演算法:

a) 把ndegree=0的點A放到一個Queue中;

b) 把點A出隊,然後對所有的與A鄰接的點的indegree減一;

c) 把新的ndegree=0的點入隊;

d) 重複上面的步驟


虛擬碼實現:

void TopSort(Graph G)
{
	Queue Q;
	int Num=0;
	Vertex V,W;
	Q = CreateQueue(NumVertex);
	MakeEmpty(Q);
	while(!IsEmpty(Q))
	{
		V = Dequeue(Q);
		TopNum[V] = ++Num;
		for each W adjacent to V
			if(--Indegree[W]==0)
				Enqueue(W,Q);
	}
	if(Num!=NumVertex)
		Error("Graph has a cycle!");
	Free(Q);
}

這個方法相當於是以空間換時間,引入了一個Queue。