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