1.1圖的定義:html
圖是一個由頂點集合V和一個弧集R構成的數據結構。算法
ADT Graph { 數據對象V:V是具備相同特性的數據元素的集合,稱爲頂點集。 數據關係R: R = {VR} VR = {<v,w>|v,wV且P(v,w),<v,w>表示從v到w的弧,數組
謂詞P(v,w)定義了<v,w>的意義或信息 }網絡
1.2 圖的重要術語數據結構
(1)無向圖:在一個圖中,若是任意兩個頂點構成的偶對(v,w)∈E 是無序的,即頂點之間的連線是沒有方向的,則稱該圖爲無向圖。svg
(2)有向圖:在一個圖中,若是任意兩個頂點構成的偶對(v,w)∈E 是有序的,即頂 點之間的連線是有方向的,則稱該圖爲有向圖。函數
(3)無向徹底圖:在一個無向圖中,若是任意兩頂點都有一條直接邊相鏈接,則稱該圖 爲無向徹底圖。在一個含有 n 個頂點的無向徹底圖中,有 n(n-1)/2 條邊。post
(4)有向徹底圖:在一個有向圖中,若是任意兩頂點之間都有方向互爲相反的兩條弧相 鏈接,則稱該圖爲有向徹底圖。在一個含有 n 個頂點的有向徹底圖中,有 n(n-1)條邊。測試
(5)稠密圖、稀疏圖:若一個圖接近徹底圖,稱爲稠密圖;稱邊數不多(e<nlogn)的圖爲稀疏圖。spa
(6)頂點的度、入度、出度: 頂點的度(degree)是指依附於某頂點 v 的邊數,一般記爲 TD (v)。 在有向圖中,要區別頂點的入度與出度的概念。頂點 v 的入度是指以頂點爲終點的弧的 數目,記爲 ID (v);頂點 v 出度是指以頂點 v 爲始點的弧的數目,記爲 OD (v)。 TD (v)=ID (v)+OD (v)。 能夠證實,對於具備 n 個頂點、e 條邊的圖,頂點 vi的度 TD (vi)與頂點的個數以及邊的數目知足關係:
(7)邊的權、網圖:與邊有關的數據信息稱爲權(weight)。在實際應用中,權值能夠有 某種含義。邊上帶權的圖稱爲網圖或網絡(network)。若是邊是有方向的帶權圖,則就是一 個有向網圖。
(8)路徑、路徑長度:頂點 vp到頂點 vq之間的路徑(path)是指頂點序列 vp,vi1,vi2, …, vim,vq.。其中,(vp,vi1),(vi1,vi2),…,(vim,.vq)分別爲圖中的邊。 路徑上邊的數目稱爲路徑長度。
(9)簡單路徑、簡單迴路:序列中頂點不重複出現的路徑稱爲簡單路徑。除第一個頂點 與後一個頂點以外,其餘頂點不重複出現的迴路稱爲簡單迴路,或者簡單環。
(10)子圖:對於圖 G=(V,E),G’=(V’,E’),若存在 V’是 V 的子集 ,E’是 E 的子 集,則稱圖 G’是 G 的一個子圖。
(11)連通圖、連通份量:在無向圖中,若是從一個頂點 vi到另外一個頂點 vj(i≠j)有路徑, 則稱頂點 vi和 vj是連通的。若是圖中任意兩頂點都是連通的,則稱該圖是連通圖。無向圖的 極大連通子圖稱爲連通份量。
(12)強連通圖、強連通份量:對於有向圖來講,若圖中任意一對頂點 vi和 vj(i≠j)均有 從一個頂點 vi到另外一個頂點 vj有路徑,也有從 vj到 vi的路徑,則稱該有向圖是強連通圖。有 向圖的極大強連通子圖稱爲強連通份量。
(13)生成樹:所謂連通圖 G 的生成樹,是 G 的包含其所有 n 個頂點的一個極小連通子 圖。它一定包含且僅包含 G 的 n-1 條邊。在生成樹中添加任意一條屬於原圖中的邊一定會產 生迴路,由於新添加的邊使其所依附的兩個頂點之間有了第二條路徑。若生成樹中減小任意 一條邊,則必然成爲非連通的。
(14)生成森林:在非連通圖中,由每一個連通份量均可獲得一個極小連通子圖,即一棵 生成樹。這些連通份量的生成樹就組成了一個非連通圖的生成森林。
所謂鄰接矩陣存儲結構,就是用一維數組存儲圖中頂點的信息,用矩陣表示圖中各頂點 之間的鄰接關係。
從圖的鄰接矩陣存儲方法容易看出,這種表示具備如下特色:
(1)無向圖的鄰接矩陣必定是一個對稱矩陣。所以,在具體存放鄰接矩陣時只需存放上 (或下)三角矩陣的元素便可。
(2)對於無向圖,鄰接矩陣的第 i 行(或第 i 列)非零元素(或非∞元素)的個數正好是第 i 個頂點的度 TD(vi)。
(3)對於有向圖,鄰接矩陣的第 i 行(或第 i 列)非零元素(或非∞元素)的個數正好是第 i 個頂點的出度 OD(vi)(或入度 ID(vi))。
(4)用鄰接矩陣方法存儲圖,很容易肯定圖中任意兩個頂點之間是否有邊相連;可是 ,要肯定圖中有多少條邊,則必須按行、按列對每一個元素進行檢測,所花費的時間代價很大, 這是用鄰接矩陣存儲圖的侷限性。
在用鄰接矩陣存儲圖時,除了用一個二維數組存儲用於表示頂點間相鄰關係的鄰接矩陣 外,還需用一個一維數組來存儲頂點信息,另外還有圖的頂點數和邊數。故可
將其形式描述 以下:
#define MAX_VERTEX_NUM 20 //大頂點數設爲 20
typedef char VertexType; //頂點類型設爲字符型
typedef int VRType; //邊的權值設爲整型
typedef struct { VertexType vexs[MAX_VERTEX_NUM]; //頂點表 VRType edges[MAX_VERTEX_NUM][ MAX_VERTEX_NUM]; //鄰接矩陣,即邊表 int vexnum,arcnum; //頂點數和邊數 }MGragh; //MGragh是鄰接矩陣存儲的圖類型
鄰接表(Adjacency List)是圖的一種順序存儲與鏈式存儲結合的存儲方法。鄰接表表示法相似於樹的孩子鏈表表示法。就是對於圖 G 中的每一個頂點 vi,將全部鄰接於 vi的頂點 vj鏈成 一個單鏈表,這個單鏈表就稱爲頂點 vi 的鄰接表,再將全部點的鄰接表表頭放到數組中,就構成了圖的鄰接表。
鄰接表表示的形式描述以下:
#define MAX_VERTEX_NUM 20 //大頂點數爲 20 typedef struct ArcNode{ //邊表結點 int adjvex; //鄰接點域 struct ArcNode *nextarc; //指向下一個鄰接點的指針域
//若要表示邊上信息,則應增長一個數據域info }ArcNode; typedef struct VNode{ //頂點表結點 VertexType data; //頂點域 ArcNode *firstarc; //邊表頭指針 }VNode, AdjList[MAX_VERTEX_NUM]; //AdjList 是鄰接表類型 typedef struct{ AdjList adjlist; //鄰接表 int vexnum,arcnum; //頂點數和邊數 }ALGraph; //ALGraph是以鄰接表方式存儲的圖類型
從圖的鄰接表存儲方法容易看出,這種表示具備如下特色:
(1)若無向圖中有 n 個頂點、e 條邊,則它的鄰接表需 n 個頭結點和 2e 個表結點。顯 然,在邊稀疏(e<<n(n-1)/2)的狀況下,用鄰接表表示圖比鄰接矩陣節省存儲空間,當和邊相關 的信息較多時更是如此;
(2)在無向圖的鄰接表中,頂點 vi的度恰爲第 i 個鏈表中的結點數;
(3)而在有向圖中,第 i 個鏈表中的結點個數只是頂點 vi的出度,爲求入度,必須遍歷整個鄰接表。在全部鏈表中其鄰接點域的值爲 i 的結點的個數是頂點 vi的入度。 有時,爲了便於肯定頂點的入度或以頂點 vi爲頭的弧,能夠創建一個有向圖的逆鄰接表, 即對每一個頂點 vi 創建一個連接以 vi爲頭的弧的鏈表。在創建鄰接表或逆鄰接表時,若輸入的頂點信息即爲頂點的編號,則創建鄰接表的複雜度爲 O(n+e),不然,須要經過查找才能獲得頂點在圖中位置,則時間複雜度爲 O(n·e);
(4)在鄰接表上容易找到任一頂點的第一個鄰接點和下一個鄰接點,但要斷定任意兩個 頂點(vi 和 vj)之間是否有邊或弧相連,則需搜索第 i 個或第 j 個鏈表,所以,不及鄰接矩陣方便。
圖的遍歷是指從圖中的任一頂點出發,對圖中的全部頂點訪問一次且只訪問一次。圖的 遍歷操做和樹的遍歷操做功能類似。圖的遍歷是圖的一種基本操做,圖的許多其它操做都是 創建在遍歷操做的基礎之上。圖的遍歷一般有深度優先搜索和廣度優先搜索兩種方式。
深度優先搜索(Depth_Fisrst Search)遍歷相似於樹的先根遍歷,是樹的先根遍歷的推廣。 假設初始狀態是圖中全部頂點不曾被訪問,則深度優先搜索可從圖中某個頂點發 v 出發, 訪問此頂點,而後依次從 v 的未被訪問的鄰接點出發深度優先遍歷圖,直至圖中全部和 v 有路徑相通的頂點都被訪問到;若此時圖中尚有頂點未被訪問,則另選圖中一個不曾被訪問的 頂點做起始點,重複上述過程,直至圖中全部頂點都被訪問到爲止。
爲了在遍歷過程當中便於區分頂點是否已被訪問,需附設訪問標誌數組 visited[0:n-1], ,其初值爲 FALSE ,一旦某個頂點被訪問,則其相應的份量置爲 TRUE。
從圖的某一點 v 出發,遞歸地進行深度優先遍歷的過程算法以下。
void DFSTraverse (Graph G) { //深度優先遍歷圖 G
for (v=0; v<G.vexnum; ++v)
visited[v] = FALSE; //訪問標誌數組初始化
for (v=0; v<G.vexnum; ++v) if (!visited[v]) DFS(G,v); //對還沒有訪問的頂點調用 DFS } void DFS(Graph G,int v ) { //從第 v 個頂點出發遞歸地深度優先遍歷圖 G visited[v]=TRUE;Visit(v); //訪問第 v 個頂點 for (w=FisrtAdjVex(G,v);w>=0; w=NextAdjVex(G,v,w)) if (!visited[w]) DFS(G,w); //對 v 的還沒有訪問的鄰接頂點 w 遞歸調用 DFS }
分析上述算法,在遍歷時,對圖中每一個頂點至多調用一次 DFS 函數,由於一旦某個頂點 被標誌成已被訪問,就再也不從它出發進行搜索。所以,遍歷圖的過程實質上是對每一個頂點查找其鄰接點的過程。
時間複雜度取決於所採用的存儲結構。當用二維數組表示鄰接矩陣 圖的存儲結構時,查找每一個頂點的鄰接點所需時間爲 O(n2) ,其中 n 爲圖中頂點數。而當以 鄰接表做圖的存儲結構時,找鄰接點所需時間爲 O(e),其中 e 爲無向圖中邊的數或有向圖中 弧的數。由此,當以鄰接表做存儲結構時,深度優先搜索遍歷圖的時間複雜度爲 O(n+e) 。
廣度優先搜索(Breadth_First Search) 遍歷相似於樹的按層次遍歷的過程。
假設從圖中某頂點 v 出發,在訪問了 v 以後依次訪問 v 的各個不曾訪問過的鄰接點,然 後分別從這些鄰接點出發依次訪問它們的鄰接點,並使「先被訪問的頂點的鄰接點」先於「後被 訪問的頂點的鄰接點」被訪問,直至圖中全部已被訪問的頂點的鄰接點都被訪問到。若此時圖 中尚有頂點未被訪問,則另選圖中一個不曾被訪問的頂點做起始點,重複上述過程,直至圖 中全部頂點都被訪問到爲止。換句話說,廣度優先搜索遍歷圖的過程當中以 v 爲起始點,由近 至遠,依次訪問和 v 有路徑相通且路徑長度爲 1,2,…的頂點。
廣度優先搜索和深度優先搜索相似,在遍歷的過程當中也須要一個訪問標誌數組。而且, 爲了順次訪問路徑長度爲 二、 三、…的頂點,需附設隊列以存儲已被訪問的路徑長度爲 一、 二、… 的頂點。
從圖的某一點 v 出發,遞歸地進行廣度優先遍歷的過程算法以下。
void BFSTraverse (Graph G) { //按廣度優先非遞歸遍歷圖 G,使用輔助隊列 Q
for (v=0; v<G.vexnum; ++v)
visited[v] = FALSE; //訪問標誌數組初始化
for (v=0; v<G.vexnum; ++v) if (!visited[v]) BFS(G, v); //對還沒有訪問的頂點調用 BFS } void BFS (Graph G,int v) { InitQueue(Q); //置空的輔助隊列 Q visited[v]=TRUE; Visit(v); //訪問 v EnQueue(Q,v); //v入隊列 while (!QueueEmpty(Q)) { DeQueue(Q,u); //隊頭元素出隊並置爲u for(w=FistAdjVex(G,u); w>=0; w=NextAdjVex(G,u,w)) if (!visited[w]){ visited[w]=TRUE; Visit(w); EnQueue(Q,w); //u還沒有訪問的鄰接頂點 w 入隊列 Q
}
}
}
分析上述算法,每一個頂點至多進一次隊列。遍歷圖的過程實質是經過邊或弧找鄰接點的過程,所以廣度優先搜索遍歷圖的時間複雜度和深度優先搜索遍歷相同,二者不一樣之處僅僅在於對頂點訪問的順序不一樣。
4.1.1 最小生成樹的基本概念
連通圖的一次遍歷所通過的邊的集合及圖中全部頂點的集合就構成了該圖的一棵生成樹,對連通圖的不一樣遍歷,就可能獲得不一樣的生成樹。若是無向連通圖是一個網,那麼它的全部生成樹中必有一棵邊的權值總和最小的生成樹,咱們稱這棵生成樹爲最小生成樹,簡稱爲最小生成樹。
由生成樹的定義可知,無向連通圖的生成樹不是惟一的。
對於有 n 個頂點的無向連通圖,不管其生成樹的形態如何,所 有生成樹中都有且僅有 n-1 條邊。
4.1.2 Prim 算法
假設 G=(V,E)爲一網圖,其中 V 爲網圖中全部頂點的集合,E 爲網圖中全部帶權邊的集合。設置兩個新的集合 U 和 T,其中集合 U 用於存放 G 的最小生成樹中的頂點,集合 T 存放 G 的最小生成樹中的邊。令集合 U 的初值爲 U={u1}(假設構造最小生成樹時,從頂點 u1 出發),集合 T 的初值爲 T={}。
Prim 算法的思想是:從全部 u∈U,v∈V-U 的邊中,選取具備最小權值的邊(u,v),將 頂點 v 加入集合 U 中,將邊(u,v)加入集合 T 中,如此不斷重複,直到 U=V 時,最小生成樹構造完畢,這時集合 T 中包含了最小生成樹的全部邊。
Prim算法可用下述過程描述,其中用 wuv表示頂點 u 與頂點 v 邊上的權值。
(1)U={u1},T={};
(2)while (U≠V)do
(u,v)=min{wuv;u∈U,v∈V-U }
T=T+{(u,v)}
U=U+{v}
(3)結束。
Prim算法的時間複雜度爲 O(n2),與網中的邊數無關,所以適用於求邊稠密的網的小生成樹。
4.1.3 Kruskal 算法
Kruskal 算法是一種按照網中邊的權值遞增的順序構造最小生成樹的方法。其基本思想 是:設無向連通網爲 G=(V,E),令 G 的小生成樹爲 T,其初態爲 T=(V,{}),即開 始時,小生成樹 T 由圖 G 中的 n 個頂點構成,頂點之間沒有一條邊,這樣 T 中各頂點各自構成一個連通份量。而後,按照邊的權值由小到大的順序,考察 G 的邊集 E 中的各條邊。若 被考察的邊的兩個頂點屬於 T 的兩個不一樣的連通份量,則將此邊做爲小生成樹的邊加入到 T 中,同時把兩個連通份量鏈接爲一個連通份量;若被考察邊的兩個頂點屬於同一個連通分 量,則捨去此邊,以避免形成迴路,如此下去,當 T 中的連通份量個數爲 1 時,此連通份量便 爲 G 的一棵小生成樹。
Kruskal 算法需對 e 條邊按權值進行排序,時間複雜度爲 O(eloge)(e 爲網中邊的數目),因 此適用於求邊稀疏的網的小生成樹。
1.從一個源點到其它各點的短路徑
單源點的短路徑問題:給定帶權有向圖 G=(V,E)和源點 v∈V,求從 v 到 G 中其 餘各頂點的短路徑。
(1)求從源點到其他各點的短路徑的算法的基本思想: 依短路徑的長度遞增的次序求得各條路徑。
①假設圖中所示爲從源點到其他各點之間的短路徑,則在這些路徑中,必然存在一條長度短者。其中,從源點到頂點 v 的短路徑是全部短路徑中長度短者。
②路徑長度短的短路徑的特色: 在這條路徑上,一定只含一條弧,而且這條弧的權值小。
③下一條路徑長度次短的短路徑的特色: 它只可能有兩種狀況:或者是直接從源點到該點(只含一條弧);或者是,從源點通過頂點 v1,再到達該頂點(由兩條弧組成)。
④再下一條路徑長度次短的短路徑的特色: 它可能有三種狀況:或者是,直接從源點到該點(只含一條弧);或者是,從源點通過頂點 v1,再到達該頂點(由兩條弧組成);或者是,從源點通過頂點 v2,再到達該頂點。
⑤其他短路徑的特色: 它或者是直接從源點到該點(只含一條弧);或者是,從源點通過已求得短路徑的頂點, 再到達該頂點。
(2)迪傑斯特拉(Dijkstra)算法
迪傑斯特拉提出的一個按路徑長度遞增的次序產生短路徑的算法。
該算法的基本思想是:設置兩個頂點的集合 S 和 T=V-S,集合 S 中存放已找到短路 徑的頂點,集合 T 存放當前還未找到短路徑的頂點。初始狀態時,集合 S 中只包含源點 v0, 而後不斷從集合 T 中選取到頂點 v0路徑長度短的頂點 u 加入到集合 S 中,集合 S 每加入一 個新的頂點 u,都要修改頂點 v0到集合 T 中剩餘頂點的短路徑長度值,集合 T 中各頂點新 的短路徑長度值爲原來的短路徑長度值與頂點 u 的短路徑長度值加上 u 到該頂點的路 徑長度值中的較小值。此過程不斷重複,直到集合 T 的頂點所有加入到 S 中爲止。
2.每一對頂點之間的短路徑
解決這個問題的一個辦法是:每次以一個頂點爲源點,重複招待迪傑斯特拉算法 n 次。 這樣,即可求得每一結頂點的短路徑。總的執行時間爲 O(n3)。
弗洛伊德(Floyd)算法:
弗洛伊德算法仍從圖的帶權鄰接矩陣 cost 出發,其基本思想是:
假設求從頂點vi到vj的短路徑。若是從vi到vj有弧,則從vi到vj存在一條長度爲arcs i 的路徑,該路徑不必定是短路徑,尚需進行 n 次試探。首先考慮路徑(vi, v0, vj)是否存在 (即判別弧(vi, v0)和(v0, vj
1.AOV 網
全部的工程或者某種流程能夠分爲若干個小的工程或階段,這些小的工程或階段就稱爲活動。若以圖中的頂點來表示活動,有向邊表示活動之間的優先關係,則這樣活動在頂點上 的有向圖稱爲 AOV 網。在 AOV 網中,若從頂點 i 到頂點 j 之間存在一條有向路徑,稱頂點 i 是頂點 j 的前驅,或者稱頂點 j 是頂點 i 的後繼。若<i,j>是圖中的弧,則稱頂點 i 是頂點 j 的 直接前驅,頂點 j 是頂點 i 的直接後驅。
2.拓撲排序
AOV 網所表明的一項工程中活動的集合顯然是一個偏序集合。爲了保證該項工程得以順利完成,必須保證 AOV 網中不出現迴路;不然,意味着某項活動應以自身做爲可否開展的先決條件,這是荒謬的。 測試 AOV 網是否具備迴路(便是否是一個有向無環圖)的方法,就是在 AOV 網的偏序集合下構造一個線性序列,該線性序列具備如下性質:
① 在 AOV 網中,若頂點 i 優先於頂點 j ,則在線性序列中頂點 i 仍然優先於頂點 j;
② 對於網中原來沒有優先關係的頂點 i 與頂點 j,在線性序列中也創建一個前後關係, 或者頂點 i 優先於頂點 j ,或者頂點 j 優先於 i。
知足這樣性質的線性序列稱爲拓撲有序序列。構造拓撲序列的過程稱爲拓撲排序。也可 以說拓撲排序就是由某個集合上的一個偏序獲得該集合上的一個全序的操做。
若某個 AOV 網中全部頂點都在它的拓撲序列中,則說明該 AOV 網不會存在迴路,這時 的拓撲序列集合是 AOV 網中全部活動的一個全序集合。顯然,對於任何一項工程中各個活動 的安排,必須按拓撲有序序列中的順序進行纔是可行的。
3.拓撲排序算法
對 AOV 網進行拓撲排序的方法和步驟是:
①從 AOV 網中選擇一個沒有前驅的頂點(該頂點的入度爲 0)而且輸出它;
②從網中刪去該頂點,而且刪去從該頂點發出的所有有向邊;
③重複上述兩步,直到剩餘的網中再也不存在沒有前驅的頂點爲止。
這樣操做的結果有兩種:一種是網中所有頂點都被輸出,這說明網中不存在有向迴路; 另外一種就是網中頂點未被所有輸出,剩餘的頂點均不前驅頂點,這說明網中存在有向迴路。
對一個具備 n 個頂點、e 條邊的網來講,整個算法的時間複雜度爲 O(e+n)。
1.AOE 網
若在帶權的有向圖中,以頂點表示事件,以有向邊表示活動,邊上的權值表示活動的開 銷(如該活動持續的時間),則此帶權的有向圖稱爲 AOE 網。
AOE網具備如下兩個性質:
①只有在某頂點所表明的事件發生後,從該頂點出發的各有向邊所表明的活動才能開始。
② 只有在進入一某頂點的各有向邊所表明的活動都已經結束,該頂點所表明的事件才能 發生。
2.關鍵路徑
因爲 AOE 網中的某些活動可以同時進行,故完成整個工程所必須花費的時間應該爲源點 到終點的大路徑長度(這裏的路徑長度是指該路徑上的各個活動所需時間之和)。具備大 路徑長度的路徑稱爲關鍵路徑。關鍵路徑上的活動稱爲關鍵活動。關鍵路徑長度是整個工程 所需的短工期。這就是說,要縮短整個工期,必須加快關鍵活動的進度。
利用 AOE 網進行工程管理時要需解決的主要問題是:
①計算完成整個工程的短路徑。
②肯定關鍵路徑,以找出哪些活動是影響工程進度的關鍵。
3.關鍵路徑的肯定
爲了在 AOE 網中找出關鍵路徑,須要定義幾個參量,而且說明其計算方法。
(1)事件的早發生時間 ve[k]
ve[k]是指從源點到頂點的大路徑長度表明的時間。這個時間決定了全部從頂點發出的 有向邊所表明的活動可以開工的早時間。根據 AOE 網的性質,只有進入 vk 的全部活動< vj,vk>都結束時,vk 表明的事件才能發生;而活動< vj, vk>的早結束時間爲 ve[j]+dut(< vj, vk>)。因此計算 vk 發生的早時間的方法以下:
ve[l]=0
ve[k]=Max{ve[j]+dut(< vj, vk>)} < vj, vk>∈p[k]
其中,p[k]表示全部到達 vk的有向邊的集合;dut(< vj, vk>)爲有向邊< vj,vk>上的權值。
(2)事件的遲發生時間 vl[k]
vl[k]是指在不推遲整個工期的前提下,事件 vk 容許的晚發生時間。設有向邊< vk,vj> 表明從 vk 出發的活動,爲了避免拖延整個工期,vk 發生的遲時間必須保證不推遲從事件 vk 出 發的全部活動< vk,vj>的終點 vj 的遲時間 vl[j]。vl[k] 的計算方法以下:
vl[n]=ve[n]
vl[k]=Min{vl[j]-dut(< vk,vj>)} < vk, vj>∈s[k]
其中,s[k]爲全部從 vk 發出的有向邊的集合。
(3)活動 ai 的早開始時間 e[i]
若活動 ai 是由弧<vk,vj>表示,根據 AOE 網的性質,只有事件 vk 發生了,活動 ai 才能開 始。也就是說,活動 ai 的早開始時間應等於事件 vk的早發生時間。
所以,有: e[i]=ve[k]
(4)活動 ai 的晚開始時間 l[i]
活動 ai 的晚開始時間指,在不推遲整個工程完成日期的前提下, 必須開始的晚時間。 若 由弧< vk,vj>>表示,則 ai 的晚開始時間要保證事件 vj 的遲發生時間不拖後。
所以, 應該有: l[i]=vl[j]-dut(<vk,vj>)
根據每一個活動的早開始時間 e[i]和晚開始時間 l[i]就可斷定該活動是否爲關鍵活動, 也就是那些 l[i]=e[i]的活動就是關鍵活動,而那些 l[i]>e[i]的活動則不是關鍵活動,l[i]-e[i]的 值爲活動的時間餘量。關鍵活動肯定以後,關鍵活動所在的路徑就是關鍵路徑。
由上述方法獲得求關鍵路徑的算法步驟爲:
(1)輸入 e 條弧<j,k>,創建 AOE-網的存儲結構;
(2)從源點v0出發,令ve[0]=0,按拓撲有序求其他各頂點的早發生時間vei。 若是獲得的拓撲有序序列中頂點個數小於網中頂點數 n,則說明網中存在環,不能求關鍵路 徑,算法終止;不然執行步驟(3)。
(3)從匯點 vn 出發,令 vl[n-1]=ve[n-1],按逆拓撲有序求其他各頂點的遲發生時間 vl[i] (n-2≥i≥2);
(4)根據各頂點的 ve 和 vl 值,求每條弧 s 的早開始時間 e(s)和遲開始時間 1(s)。 若某條弧知足條件 e(s)=l(s),則爲關鍵活動。