數據結構(拓撲排序和關鍵路徑)

拓撲排序算法

拓撲序列:spa

  設G=(V,E)是一個具備n個頂點的有向圖,V中頂點序列V1,V2,......,Vn知足若從頂點Vi到Vj有一條路徑,則在頂點序列中頂點Vi必須在頂點Vj以前。則稱這樣的頂點序列爲一個拓撲序列指針

拓撲排序code

  對一個無環有向圖(AOV網)構造拓撲序列的過程blog

方法排序

  • 從AOV網中選擇一個沒有前驅的頂點(該頂點的入度爲0)並輸出它
  • 從網中刪去該頂點,而且刪去從該點發出的所有有向邊
  • 重複上述兩步,直到剩餘網中再也不存在沒有前驅的頂點爲止

由於要刪除點和邊,因此用鄰接表較爲方便索引

 1 #define MAXVEX 10
 2 
 3 //邊表結點聲明
 4 typedef struct EdgeNode{
 5     int adjvex;
 6     struct EdgeNode *next;
 7 }EdgeNode;
 8 
 9 //頂點結點聲明
10 typedef struct VertexNode{
11     int in;//頂點入度
12     int data;
13     EdgeNode *firstedge;
14 }VertexNode,AdjList[MAXVEX];
15 
16 typedef struct{
17     AdjList adjList;
18     int numVertexes,numEdges;
19 }graphAdjList,* GraphAdjList;
20 
21 //拓撲排序算法
22 //若GL無迴路,則會輸入拓撲排序序列並返回OK,不然返回ERROE
23 Status TopoLogicalSort(GraphAdjList GL){
24     EdgeNode *e;
25     int i,k,gettop;
26     int top=0;//用於棧指針下標索引
27     int count=0;//用於統計輸出頂點的個數
28     int *stact;//用於存儲入度爲0的頂點
29 
30     stact=(int *)malloc(GL->numVertexes*sizeof(int));
31 
32     for(i=0;i<GL->numVertexes;i++){
33         if(0==GL->adjList[i].in){
34             stack[++top]=i;//將度爲0的頂點下標入棧
35         }
36     }
37 
38     while(0!=top){
39         gettop=stact[top--];//出棧
40         printf("%d -> ",GL->adjList[gettop].data);
41         count++;
42         for(e=GL->adjList[gettop].firstedge;e;e=e->next){
43             k=e->adjvex;
44             //注意:下邊這個if條件是分析整個程序的要點
45             //將k號頂點鄰接頂的入度-1;由於他的前序已經刪除
46             //接着判斷-1後入度爲是否爲0,若是爲0則也入棧
47             if(!(--GL->adjList[k].in)){
48                 stack[++top]=k;
49             }
50         }
51     }
52     if(count<GL->numVertexes){//若是count小於頂點數,說明存在環
53         return ERROR;
54     }else{
55         return OK;
56     }
57 }

關鍵路徑事件

AOE網get

  在一個表示工程的帶權有向圖中,用頂點表示事件,用有向邊表示活動,用邊上的權值表示活動的持續時間,這種有向圖的邊表示活動的網。把沒入度的頂點稱爲始點或源點,沒有出度的頂點稱爲終點或匯點it

  關鍵路徑就是決定整個工程時間的路徑;由於工程中有些步驟要在一些步驟完成的基礎上才能進行,而有些則不須要,這種沒有制約的步驟就能夠同時進行,而決定整個整個工程時間的就是這種相互制約的步驟的最長時間和

思路

  正向求處事件的最先發生時間,和反向求出事件最晚發生時間;而後根據事件的etv、ltv求出活動的ete和lte;【ete==lte的活動,爲關鍵活動,關鍵活動組成的路徑是關鍵路徑】

注:etv和ltv是針對事件的,ete和lte是針對活動的;而且根據etv、ltv和活動須要的時間是能夠求出活動ete和lte的;【活動:etv就是弧尾頂點事件的最先發生時間;lte就是弧頭頂底事件ltv(最晚時間)-活動所需時間

  • etv:事件最先發生的時間,就是頂點的最先發生時間
  • ltv:事件最晚發生時間,每一個頂點對應事件最晚要開始的時間(超過此時間就會影響工程正常進度)【求得etv後根據活動所需時間反向:etv(最先時間)-活動所需時間(這裏雖然看似和lte求法類似(一個用最先發生時間減,一個用最晚時間減),但要考慮當這個頂點事件有多條出度時會求出多個ltv,這時就要取那個最小的,而lte就不會有這種顧忌)】
  • ete:活動最先開工時間,弧的最先發生時間
  • lte:活動的最晚發生時間

  1 #define MAXVEX 10
  2 
  3 //邊表結點聲明
  4 typedef struct EdgeNode{
  5     int adjvex;
  6     int weight;
  7     struct EdgeNode *next;
  8 }EdgeNode;
  9 
 10 //頂點結點聲明
 11 typedef struct VertexNode{
 12     int in;//頂點入度
 13     int data;
 14     EdgeNode *firstedge;
 15 }VertexNode,AdjList[MAXVEX];
 16 
 17 typedef struct{
 18     AdjList adjList;
 19     int numVertexes,numEdges;
 20 }graphAdjList,* GraphAdjList;
 21 
 22 int *etv,ltv;
 23 int *stact2;//用於存儲拓撲序列的棧
 24 int top2;//用於stack2的棧頂指針
 25 
 26 //拓撲排序算法
 27 //若GL無迴路,則會輸入拓撲排序序列並返回OK,不然返回ERROE
 28 Status TopoLogicalSort(GraphAdjList GL){
 29     EdgeNode *e;
 30     int i,k,gettop;
 31     int top=0;//用於棧指針下標索引
 32     int count=0;//用於統計輸出頂點的個數
 33     int *stact;//用於存儲入度爲0的頂點
 34 
 35     stact=(int *)malloc(GL->numVertexes*sizeof(int));
 36 
 37     for(i=0;i<GL->numVertexes;i++){
 38         if(0==GL->adjList[i].in){
 39             stack[++top]=i;//將度爲0的頂點下標入棧
 40         }
 41     }
 42 
 43     //初始化etv都爲0;
 44     top2=0;
 45     etv=(int *)malloc(GL->numVertexes*sizeof(int));
 46     for(i=0;i<GL->numVertexes;i++){
 47         etv[i]=0;
 48     }
 49     stack2=(int *)malloc(GL->numVertexes*sizeof(int));
 50 
 51     while(0!=top){
 52         gettop=stact[top--];//出棧
 53         //printf("%d -> ",GL->adjList[gettop].data);
 54         stack2[++top2]=gettop;//保存拓撲序列順序;頂點順序
 55         count++;
 56 
 57         for(e=GL->adjList[gettop].firstedge;e;e=e->next){//操做邊表
 58             k=e->adjvex;
 59             //注意:下邊這個if條件是分析整個程序的要點
 60             //將k號頂點鄰接頂的入度-1;由於他的前序已經刪除
 61             //接着判斷-1後入度爲是否爲0,若是爲0則也入棧
 62             if(!(--GL->adjList[k].in)){
 63                 stack[++top]=k;
 64             }
 65 
 66             //這裏刪除了一個入度爲0的點,刪除頂點(事件)的最先發生時間+邊表頂點之間的弧(活動)的權就是邊表頂點(事件)最先發生時間
 67             //可能一個頂點(事件)有多個入度,就是有多個活動完成前提,因此要等這些前提活動都完成了才能夠開始頂點(事件),因此這個頂點(事件)最先發生時間要選那個最晚的
 68             if((etv[gettop]+e->weight)>etv[k]){
 69                 etv[k]=etv[gettop]+e->weight;
 70             }
 71         }
 72     }
 73     if(count<GL->numVertexes){//若是count小於頂點數,說明存在環
 74         return ERROR;
 75     }else{
 76         return OK;
 77     }
 78 }
 79 
 80 //求關鍵路徑,GL爲有向圖;輸出GL的各項關鍵活動
 81 void CriticalPath(GraphAdjList GL){
 82     EdgeNode *e;
 83     int i,gettop,k,j;
 84     int ete,lte;
 85 
 86     //調用改進後的拓撲排序,求出etv和stack2的值
 87     TopoLogicalSort(GL);
 88 
 89     //初始化ltv都爲匯點時間;
 90     ltv=(int *)malloc(GL->numVertexes*sizeof(int));
 91     for(i=0;i<GL->numVertexes;i++){
 92         ltv[i]=etv[GL->numVertexes-1];
 93     }
 94 
 95     //從匯點倒過來逐個計算ltv
 96     while(0!=top2){
 97         gettop=stack2[top2--];//注意,第一個出棧的就是匯點
 98         for(e=GL->adjList[gettop].firstedge;e;e=e->next){//操做邊表
 99             k=e->adjvex;//弧頭頂點★★★
100             //這裏的頂點(事件)最晚發生時間爲:出度的弧頭(頂點)最晚發生時間-弧權(活動延續時間)
101             //固然可能有多個出度,爲保證每一個出度活動不延續整個工程,因此要取那個最先的弧(活動)的最晚發生時間
102             if((ltv[k]-e->weight)<ltv[gettop]){
103                 ltv[gettop]=ltv[k]-e->weight;
104             }
105         }
106     }
107 
108     //經過etv和ltv求出ete和lte
109     for(j=0;j<GL->numVertexes;e=e->next){
110         for(e=GL->adjList[j].firstedge;e;e=e->next){//操做邊表
111             k=e->adjvex;//弧頭頂點
112             //弧(活動)最先發生時間就是弧尾頂點(事件)最先發生時間
113             ete=etv[j];
114             //弧(活動)最晚發生時間就是:弧頭頂點(事件)最晚發生時間-弧權(活動延續時間)
115             lte=ltv[k]-e->weight;
116 
117             if(ete==lte){//活動的最先發生時間==活動的最晚發生時間爲關鍵活動,關鍵活動組成的路徑爲關鍵路徑
118                 printf("<V%d,V%d>length:%d  ",GL->adjList[j].data,GL->adjList[k].data,e->weight);
119             }
120         }
121     }
122 }
相關文章
相關標籤/搜索