【算法】基於AOV網的拓撲排序

  寫在前面:這篇文章在一週前就應該發的,後來因爲騰訊面試拖到現在,雖然現在下動車也有一兩個小時了,但是感覺自己好像還在路上顛簸。昨天去騰訊深圳總部面試,深圳總部啊!馬爸爸在的地方!可惜跪了。可能是因爲我主要偏軟件開發然後投了運維簡歷投偏了吧,騰訊的面試還真是玄學呢!我也感受到自己已經達到了技術瓶頸,回爐修煉~

  圖的這一章節終於快複習完了,加油加油!今天要總結的是拓撲排序,是基於AOV網的。這個拓撲排序與我們之前熟知的快速排序啊,冒泡排序啊有異同點。相同的地方是它們都能把一組數排序成有序序列。而不同的地方也恰恰在這個有序上,拓撲排序中的有序並不是什麼從大到小,或者是從小到大;而是一種先後的邏輯上的關係,具有一定的實際意義。

 知識儲備:AOV網

AOV網是一個英文縮寫,全稱是Activity On Vertex Network,即活動在頂點上的網。是一個有向不帶權重的圖,並且沒有迴路。可以用它來描述工程中各個活動的先後次序,如工業上的流程,如下圖

要製作一個產品,首先要去尋找原料,然後生成出3個部件,最後獲得成品。這個就是一個普通的生產過程,如果我們用一條有向線段從成品指向了原料,那麼這個圖中就出現了迴路,也就是出現環了。這樣的話,剛生產出來的產品卻又變成了原料,這顯然是違背【生產產品】這個工程的實際意義的。

 

 拓撲排序看上去很抽象,其實實現起來還是十分容易的。算法思路如下:

1、      選擇圖中入度爲0的頂點,輸出

2、      刪除這個頂點引出的所有邊

3、      重複步驟【1】、【2】,直到圖中所有頂點輸出爲止(也就是不再有入度爲0的點)

算法準備:

1、      圖的鄰接矩陣存儲結構(其實用鄰接表也是可以的)

2、      一個visit數組,用於標識某個頂點是否被輸出過

下面先用手工的方式演示一下拓撲排序的算法過程(基於鄰接矩陣)

 

 

 

我們的用例圖如A所示,鄰接矩陣在右下角,圖的頂點編號從1開始。

1、初始化visit數組,默認全爲0

 

 

 

2、 我們知道,在圖的鄰接矩陣存儲結構中,每一行之和代表這個頂點的出度,每一列之和代表一個頂點的入度。所以,我們選擇鄰接矩陣中第一次出現的,列和全爲0的點,顯然是編號爲1的頂點。將visit[1]的值改爲1,並且將這個頂點輸出。

 

3、 刪除頂點1,以及頂點1引出的所有邊(出度),這樣,就由圖A變爲圖B。對應的矩陣操作就是將第1行全變爲0

 

 

4、繼續尋找鄰接矩陣中列和爲0的點,此時圖中第1列和第2列都是符合條件的,但是頂點1已經訪問過了,所以選擇頂點2,即第二列。將visit[2]的值改爲1,並輸出

 

 

 

5、刪除頂點2以及它所引出的邊,即將鄰接矩陣中第2行全變爲0。由圖B變爲圖C

6、 如此往復,直到visit數組全變爲1爲止。

 

最後輸出序列爲:1 2 3 4 5

需要注意的是,拓撲排序的序列不是唯一的,我這邊是採用數組,如果採用棧的話就不一定是這個序列了。

拓撲排序的要求是圖不能有迴路,如果圖中帶環,會導致排序失敗,因爲圖會在某一個時刻找不到入度爲0的點,所以拓撲排序的一個應用是檢測圖中是否帶有迴路

下面上代碼:這段代碼帶有圖是否有環的檢測

1、圖的結構體定義(n爲頂點數、e爲邊數)

 

#define Max 100
typedef struct graph *Graph;
typedef struct graph
{
	int data[Max][Max];
	int n , e;
}graph;
//data表示鄰接矩陣,最大100 x 100
//n表示圖有n個頂點,e表示有e條邊

2、拓撲排序代碼(鄰接矩陣+數組實現)

int TopSort(Graph g)
{
	int v , i , j , count , n = 0;
	int *visit = (int*)malloc(sizeof(int) * (g->n + 1));
    //初始化visit數組
	for(i = 1 ; i <= g->n ; i++)
	{
		visit[i] = 0;
	}
	while(n != g->n)
	{
        //遍歷鄰接矩陣,尋找入度爲0的點,若找到,退出循環
		for(i = 1 ; i <= g->n ; i++)
		{
			count = 0;
			for(j = 1 ; j <= g->n ; j++)
			{
				count += g->data[j][i];
			}
			if(count == 0 && visit[i] == 0)
			{
				v = i;
				n++;
				visit[v] = 1;
				break;
			}
		}
		//如果某一時刻遍歷完全部的鄰接矩陣也沒有找到入度爲0的點
        //此時,i與j指向最後一個元素,勢必相等,表示發現環。
		if(i == j)//發現環
		{
			return 1;
		}
		
		printf("%d ",v);
		
        //將入度爲0的點v所在的那一行全部變成0,因爲列已經全0,表示刪除該點
		for(i = 1 ; i <= g->n ; i++)
		{
			g->data[v][i] = 0;
		}
	}
	return 0;
}

 

最後附上一張騰訊招聘現場吧~勉勵自己繼續加油!

 

 

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