数据结构-----图的拓扑排序和关键路径算法
部分圖片取自:http://www.cnblogs.com/navorse/articles/1893863.html
在介紹拓撲排序和關鍵路徑之前,先引入AOE網絡的概念:
該圖為一個AOE網,頂點表示事件,如v1,v2,v3...。弧表示活動,如a1,a2,a3...,而每一條邊表示的值為完成該活動所需的時間。
比如上圖中,完成活動a1需要6個單位的時間,完成活動a3需要5個單位的時間。
AOE網絡是一個加權有向圖,即每一條邊都是帶方向且帶有權值的。對于一個有向邊,箭頭指向的點為終點,另一個點則是起點。
頂點的入度是指所有以該頂點為終點的有向邊的個數。如上圖中以頂點v5為終點的有向邊為a4和a5,所以頂點v5的入度為2。
頂點的出度是指所有以該頂點為起點的有向邊的個數。如上圖中以頂點v1為起點的有向邊為a1,a2和a3,所以頂點v1的出度為3。
我們把入度為0的頂點叫做源點,出度為0的頂點叫做匯點。上圖中v1是源點,v9是匯點。
可以理解為從源點出發,雖然中間會有很多不同的分岔口,但最終都會到達匯點。一個AOE網絡至少包含一個源點和一個匯點。
概念說完了,接下來討論一下AOE網絡的性質:
對于AOE網絡中的某一個頂點(事件)來說,只有當所有以該頂點為終點的有向邊(活動)都完成后,該頂點代表的事件才能夠發生。
同時只有某一個頂點(事件)發生后,以該頂點為起點的有向邊(活動)才能夠開始。
對于圖中的頂點(事件)v5來說,只有邊(活動)a4和a5完成后,事件v5才可以發生,邊(活動)a7和a8才能開始。
一個AOE網絡很形象的描述了在一個大的工程中的每一個子工程的前驅工程是什么,以及其后續工程是什么。同時根據每一個活動(邊)的權值,我們可以知道某一事件發生的時間是什么。
接下來說一下拓撲排序,實現拓撲排序的步驟如下:
步驟1:找到AOE網絡中入度為0的頂點,輸出它。如果找不到,停止排序。
步驟2:刪除所有與該點關聯的邊,重復步驟1。
步驟3:如果輸出頂點個數小于總頂點個數,則證明圖中存在環,沒有關鍵路徑
拓撲排序的結果叫做拓撲序列,一個AOE網絡的拓撲序列不止一種
接下來討論關鍵路徑,先明確幾個名詞。
關鍵路徑:從源點到匯點的路徑長度最大的路徑叫關鍵路徑。
事件的最早發生時間ve:因為對于某一個事件(頂點)來說,其入度可能大于1,所以從源點到達該頂點的路就不止一條。對于其中一條路來說,經過的所有邊的權值的和就是對這條路來說,到達該頂點需要的時間。把到達該頂點所有路的時間進行比較,取最大值,就是該事件的最早發生時間。
事件的最遲發生時間vl:值在不耽誤整個工程完成時間的前提下,某一事件最晚的發生時間。這個通常比較難理解,舉個例子說明一下。
比如說有三個事件a,b,c。a和b是c的前驅事件,完成活動ac需要的時間是2小時,完成活動bc需要的時間是3小時。假設現在時間是下午1點,此時事件a和事件b都已經發生,根據前面的描述可知,活動ac和bc都可以開始了。但是因為bc需要完成的時間長,所以事件c發生的時間是下午4點。那么因為ac只需兩個小時就可以完成,那么事件a發生的時間可以是下午2點,再加上ac完成時間2個小時也是下午4點,并沒有耽誤到事件c的發生,這就是所謂的最晚發生時間。所以事件a的最早發生時間是下午1點,最晚發生時間是下午2兩點。
活動開始的最早時間e:因為事件發生的同時與該事件關聯的活動也就可以開始。所以一個活動(邊)的最早開始時間和其起點所代表的事件的最早開始時間相同。
活動開始的最晚時間l:事件的最晚發生時間是將事件推遲,活動的最晚開始時間是將活動推遲,二者類似。
關鍵活動:e和l相等的活動稱為關鍵活動。
所以求關鍵路徑的問題就是求所有關鍵活動的問題。
示例如下:
接下來就是拓撲排序和關鍵路徑的實現了。
首先需要考慮的是AOE網絡的存儲,需要一個圖的類,可以用鄰接表的方式實現。
它存在一個公有函數eraseEdge(int v1, int v2),可以刪除邊<v1,v2>。
一個公有函數inDegree(int v),返回頂點v的入度。
一個公有函數getFirstNode(int v),返回與頂點v關聯的邊的鄰接表的表頭指針。
其次需要考慮用什么存儲拓撲序列,考慮到在求拓撲序列時匯點是最后一個被存儲,而最晚發生時間是從后向前求得,匯點應該首先被彈出,所以選用棧來存儲。
同時保存入度為0的頂點時也可以用棧來存儲,不同的存儲方式得出的序列不同(拓撲序列不止一個)。
最后需要兩個數組,一個存儲事件的最早發生時間,一個存儲事件的最晚發生時間。
#define MAX_VEX 10 linearStack<int> stack2;bool TopologicalSort(linkedWDigigraph theGraph, int *pEtv) {linearStack<int> stack1;for(int i = 0; i<=MAX_VEX; ++i) //初始化最早發生時間pEtv[i] = 0;for(int i = 1; i<=MAX_VEX; ++i){if(theGraph.inDegree(i) == 0) //首先尋找入度為0的頂點stack1.push(i);}int v1;int nCnt(0);chainNode<int>* pNode;while(!(stack1.empty())){v1 = stack1.top();stack1.pop();stack2.push(v1); //保存拓撲序列if(++nCnt == MAX_VEX) //判斷是否有環break;pNode = theGraph.getFirstNode(v1);int v2, weight;while(pNode != NULL){v2 = pNode->element;weight = pNode->weight;pNode = pNode->next;theGraph.eraseEdge(v1, v2);if(theGraph.inDegree(v2) == 0) //刪除和頂點v1關聯的邊,再次尋找入度為0的頂點stack1.push(v1);if(pEtv[v2] < weight + pEtv[v1]) //更新最大值pEtv[v2] = weight + pEtv[v1];}}return nCnt == MAX_VEX; //判斷是否有環 }stack2存儲著拓撲序列,供關鍵路徑函數使用:
總結
以上是生活随笔為你收集整理的数据结构-----图的拓扑排序和关键路径算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构-----AVL树的插入删除操作
- 下一篇: 数据结构-----红黑树的插入操作