0.1) 本文總結於 資料結構與演算法分析, 原始碼均為原創, 旨在理解 拓撲排序的思想並用原始碼加以實現;
0.2) 圖論演算法基礎知識,
【1】圖論若干相關定義
1.1)圖G定義:一個圖G=(V,E)由頂點及集V 和 邊集E組成, 每一條邊就是一個點對(v, w);
1.2)邊鄰接:若且唯若(v,w)∈E, 在無向圖中, w和v鄰接,且v也和w鄰接;(還有第3中成分:邊的權值)
1.3)路徑: 一條路徑是一個頂點序列 w1, w2,…,wn, 使得(wi,wi+1)∈E,這樣一條路徑長是該路徑上的邊數;
1.4)簡單路徑:其上的所有頂點都是互異的,但第一個頂點和最後一個頂點可能相同;
1.5)連通圖(對於無向圖而言): 如果在一個無向圖中從每一個頂點到每個其他頂點都存在一條路徑,則稱該無向圖是連通的;
1.6)強連通圖(對於有向圖而言):具有無向圖連通性質的有向圖稱為是強連通的;
1.7)基礎圖:去掉有向圖上的方向所形成的圖;
1.8)弱連通圖:如果有向圖不是強連通的, 但基礎圖是連通的, 那麼該圖稱為是弱連通的;
1.9)完全圖: 是指每一對頂點間都存在一條邊的圖;
【2】圖的表示
2.1)鄰接矩陣:對於每條邊(u, v), 我們設定 A[u][v]=1,否則數組的元素為0;如果邊是有權的,設定A[u][v] 等於該權值 且用一個很大或者很小的權作為標記表示不存在的邊;(∞)
2.1.1)鄰接表的空間需求是 Θ(|V|^2);如果圖是稠密的:|E| = Θ(|V|^2) , 則鄰接矩陣是合適的表示方法;如果在大部分應用中,圖都是稀疏的;
2.2)鄰接表(圖的標準表示方法):如果圖是稀疏的, 則使用鄰接表來表示。對每一個頂點,我們使用一個表存放所有鄰接的頂點。此時的空間需求為 O(|E| + |V|);
2.2.1)引入散列表:在應用中,頂點都是名字而不是數字, 這些名字在編譯時間是未知的。由於我們不能夠通過未知名字為一個數組做索引, 因此我們必須提供從名字到數位映射。完成這項工作最容易的 方法是使用散列表, 在該散列表中我們對每個頂點儲存一個名字以及一個範圍在1到 |V| 之間的內部編號;
2.2.2)鄰接表的一個荔枝:
【1】拓撲排序(有向無圈圖才有資格談拓撲排序,有向且要無圈)
1.1)拓撲排序定義: 拓撲排序是對有向無圈圖的頂點的一種排序, 它使得如果存在一條從 vi 到 vj的路徑,那麼在排序中 vj出現在 vi的後面;(有向無圈圖:一個有向圖沒有圈, 圈:滿足w1=wn 且長度至少為1個一條路徑)
1.2)拓撲排序應用(有點像聯機演算法): 課程選修順序, 如有向邊(v, w)表明課程v 必須在課程w 選修前修完;(顯然,如果圖含有圈, 那麼拓撲排序是不可能的)
1.3)一個簡單的求拓撲排序的演算法是: 先找出任意一個沒有入邊的頂點。 然後我們顯示出該頂點,並將它和他的邊一起從圖中刪除;
1.4)改進後的求拓撲排序的演算法:
1.4.1)演算法描述:通過將所有入度為0 的頂點放在一個特殊的盒子中而避免這種無效的勞動。此時 FindNewVertexOfIndegreeZero 函數返回(並刪除)盒子中的 任一頂點。當我們降低這些鄰接頂點的入度時,檢查每一個頂點並在它的入度 降為0 時把它放入盒子中;
1.4.2)實現盒子(引入了隊列):為實現這個盒子,我們使用一個棧或者隊列;首先, 對每一個頂點計算它的入度。然後,將所有入度為0的頂點放入一個初始為空白的隊列中。當隊列不空時,刪除一個頂點v, 並將與v 鄰接的所有頂點的入度減1。只要一個頂點的入度降為0, 就把該頂點放入隊列中。此時,拓撲排序就是頂點出隊的順序;
1.4.3)我們的假設:我們假設圖已經被讀到一個鄰接表中且入度已計算並被放入一個數組內;
1.4.4)時間複雜度是: O(|E| + |V|);
【2】拓撲排序原始碼 + printing results:
2.1)download source code:
https://github.com/pacosonTang/dataStructure-algorithmAnalysis/tree/master/chapter9/p219_topSort
2.2)source code at a glance(for full code, please download source code following the given link above):
#include "adjTable.h"
#include "queue.h"
void topSort(AdjTable* adj, int size, int* indegreeArray, Queue queue)
{
int i;
int counter;
ElementType vertex;
ElementType adjVertex;
AdjTable temp;
AdjTable temp1;
for(i=0; i
if(!indegreeArray[i]) // find the element who has zero indegree in adjoining table
enQueue(queue, i); //let the element enter the queue
printf("\t topSorting sequence is as follows: ");
counter = 0;
while(!isEmpty(queue))
{
vertex = deQueue(queue); // if the queue is empty, conducting departing queue
temp = adj[vertex]->next;
while(temp)
{
adjVertex = temp->index; // each adjVertex adjacent to vertex
if(--indegreeArray[adjVertex] == 0)
enQueue(queue, adjVertex);
temp1 = temp->next;
free(temp);
temp = temp1;
}
printf("vertex[%d] ", vertex+1);
counter++;
}
if(counter != size)
Error("failed top sorting, for graph has a cycle, from func topSort !");
disposeQueue(queue);
printf("\n\t");
}
// initializing indegree array with given size
int *initIndegree(int size)
{
int *indegreeArray;
int i;
indegreeArray = (int*)malloc(sizeof(int) * size);
if(!indegreeArray)
{
Error("failed intialization ,for out of space ,from func initIndegree");
return NULL;
}
for(i=0; i < size; i++)
indegreeArray[i] = 0;
return indegreeArray;
}
int main()
{
AdjTable* adj;
int *indegreeArray;
Queue queue;
int size = 7;
int i;
int j;
int column = 3;
int adjTable[7][3] =
{
{2, 4, 3},
{4, 5, 0},
{6, 0, 0},
{6, 7, 3},
{4, 7, 0},
{0, 0, 0},
{6, 0, 0}
};
printf("\n\n\t ====== test for topological sorting with adjoining table ======\n");
adj = initAdjTable(size);
indegreeArray = initIndegree(size);
queue = initQueue(size);
printf("\n\n\t ====== the initial adjoining table is as follows:======\n");
for(i = 0; i < size; i++)
for(j = 0; j < column; j++)
if(adjTable[i][j])
{
insertAdj(adj, adjTable[i][j]-1, i); // insertAdj the adjoining table over
indegreeArray[adjTable[i][j]-1]++; // update indegree of elements
}
printAdjTable(adj, size);
// topSorting starts
//void topSort(AdjTable* adj, int size, int* indegreeArray, Queue queue)
topSort(adj, size, indegreeArray, queue);
return 0;
}
2.3)printing results: