程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 7.9 拓撲排序和關鍵路徑

7.9 拓撲排序和關鍵路徑

編輯:關於C語言

拓撲排序和關鍵路徑是基於無環的有向圖。

主要用來表示工程進度中各個事件之間的關系。

 拓撲排序和關鍵路徑 使用鄰接表存儲數據,最小生成樹和最短路徑用 鄰接矩陣 存儲數據。

1、拓撲排序

AOV網:在一個表示工程的有向圖中,用固定點表示活動,用弧表示活動之間的優先級關系,這樣的有向圖為頂點表示活動的網,我們稱之為AOV網Activity On Vertex Network)

拓撲排序:是將一個AOV網的各個頂點按一定順序排列,要求滿足若存在<Vi,Vj>,則排序中的頂點Vi必在頂點Vj之前。對於同一幅圖,拓撲排序可有多個拓撲排序。

在顯示生活中,用到的拓撲排序的例子:

        學校課程布置圖,要先修完一些基礎課,才可以繼續修專業課,以計算機軟件專業為例,在《程序設計基礎》和《離散數學》課程學完之前就不能開始學習《數據結構》,這些先決條件定義了課程之間的優先領先)關系。

算法:

1)從有向圖中選擇一個無前驅入度為0)的頂點輸出。

2)刪除此頂點,並刪除已此頂點為為尾的弧。

3)重復1),2)步驟,知道輸出全部頂點或者AOV網中不存在入度為0的頂點。

具體實現代碼:

    用棧來保存入度為0的頂點,和更新後入度為0的頂點

#include <stdio.h>
#define MAXVEX 100
typedef struct EdgeNode   /*邊表節點*/
{
int adjvex;       /*鄰接點域,存儲該頂點對應的下表*/
int weight;       /*用於存儲權值,對於非網圖可以不需要*/
struct EdgeNode *next;  /*鏈域,指向下一個鄰節點*/
}EdgeNode;
typedef struct VertexNode   /*頂點表節點*/
{
int in;          /*頂點入度*/
int data;       /*頂點域,存儲頂點信心*/
EdgeNode *firstedge; /*邊表頭指針*/
}VertexNode, Adjlist[MAXVEX];
typedef struct
{
Adjlist adjList;
int numVertexes;
int numEdges;  /*圖中當前頂點數和邊數*/
}graphAdjList, *GraphAdjList;
/*拓撲排序,若GL無回路,則輸出拓撲排序序列並返回OK,若有回路返回ERROR*/
int TopoLogicalSort(GraphAdjList GL)
{
EdgeNode *e;
int i,k,gettop;
int top = 0;   /*用於棧指針下表*/
int count = 0; /*用於統計輸出頂點的個數*/
int *stack;  /*建棧用於存儲入度為0的頂點*/
stack = (int*)malloc(GL->numVertexes *sizeof(int));  //動態分配內存,大小為n個頂點(最多有n個頂點入度為0,因為圖總共有n個頂點)
for(i = 0; i<GL->numVertexes; i++)
if(GL->adjList[i].in == 0)  /*將入度為0的頂點入棧*/
stack[++top] = i;
while(top != 0)
{
gettop = stack[top--];  /*出棧*/
printf("%d -> ",GL->adjList[gettop].data); /*打印此頂點*/
count++; /*統計輸出頂點數*/
for(e=GL->adjList[gettop].firstedge; e!; e=e->next)
{ /*對此頂點弧表遍歷*/
k = e->adjvex;
if(--GL->adjList[k].in == 0)  //將k號頂點鄰接點的入度減1,且減1後,入度為0的頂點需要存到棧中
stack[++top] = k;
}
}
if(count < GL->numVertexes)  /*如果count小於頂點數,說明存在環*/
return -1;
else
return 0;
}

 

分析整個算法,對一個具有n個頂點e條弧的AOV網來說,將入度為0的頂點入棧的時間復雜度為O(n),而之後的while循環中,每個頂點入一次棧,出一次棧,入度減1的操作執行了e次,所以整個算法時間復雜度為O(n+e)。

2、關鍵路徑

拓撲排序是解決一個工程能否順序進行的問題,但有時還需解決工程完成需要的最短時間。

AOE網:在一個表示工程帶權的有向圖中,用頂點表示事件,用有向邊表示活動,用邊上的權值表示活動的持續時間,這種用有向圖的邊表示活動的網,我們稱之為AOE網Activity On Edge Network)

用ve(i)表示事件即頂點)i的最早開始時間,用vl(i)表示事件i的最開始時間,如果活動k由弧<m,n>表示,用dut(<m,n>)表示活動的持續時間,則有:

e(k) = ve(m)

l(k) = vl(n) - dut(<m,n>)

求解關鍵路徑的具體算法假設圖中有n個頂點)

(1) 從開始頂點V0出發,假設ve(0)=0,然後按照拓撲有序求出其他各頂點i的最早開始時間ve(i),如果得到拓撲序列中頂點數目小於圖中的頂點數,則表示圖中存在回路,算法結束,否則繼續執行。

2)從結束頂點Vn出發,假設vl(n-1) = ve(n-1);然後求出各頂點i的最晚發生時間。

3)根據頂點的最早發生時間,和最晚發生時間,依次求出出每條弧的最早開始時間和最晚開始時間,如果兩只相等,則為關鍵活動。關鍵活動組成的路徑則為關鍵路徑。

具體實現代碼:

 

#include <stdio.h>
#define MAXVEX 100
/*首先聲明幾個全局變量*/
int *etv,*ltv;  /*時間的最早發生時間和最遲發生時間*/
int *stack2;  /*用於存儲拓撲序列的棧*/
int top2;    /*用於stack2的指針*/
typedef struct EdgeNode   /*邊表節點*/
{
int adjvex;       /*鄰接點域,存儲該頂點對應的下表*/
int weight;       /*用於存儲權值,對於非網圖可以不需要*/
struct EdgeNode *next;  /*鏈域,指向下一個鄰節點*/
}EdgeNode;
typedef struct VertexNode   /*頂點表節點*/
{
int in;          /*頂點入度*/
int data;       /*頂點域,存儲頂點信心*/
EdgeNode *firstedge; /*邊表頭指針*/
}VertexNode, Adjlist[MAXVEX];
typedef struct
{
Adjlist adjList;
int numVertexes;
int numEdges;  /*圖中當前頂點數和邊數*/
}graphAdjList, *GraphAdjList;
/*拓撲排序,若GL無回路,則輸出拓撲排序序列並返回OK,若有回路返回ERROR*/
int TopoLogicalSort(GraphAdjList GL)
{
EdgeNode *e;
int i,k,gettop;
int top = 0;   /*用於棧指針下表*/
int count = 0; /*用於統計輸出頂點的個數*/
int *stack;  /*建棧用於存儲入度為0的頂點*/
stack = (int*)malloc(GL->numVertexes *sizeof(int));  //動態分配內存,大小為n個頂點(最多有n個頂點入度為0,因為圖總共有n個頂點)
for(i = 0; i<GL->numVertexes; i++)
if(GL->adjList[i].in == 0)  /*將入度為0的頂點入棧*/
stack[++top] = i;
top2 = 0;
etv = (int*) malloc(GL->numVertexes*sizeof(int));  /*事件的最早發生時間*/
for(i=0;i<GL->numVertexes; i++)
etv[i] = 0;
stack2 = (int*) malloc(GL->numVertexes*sizeof(int)); /*初始化*/
while(top != 0)
{
gettop = stack[top--];  /*出棧*/
printf("%d -> ",GL->adjList[gettop].data); /*打印此頂點*/
stack2[++top2] = gettop;  /*將彈出的頂點序列號壓入拓撲序列的棧中*/
count++; /*統計輸出頂點數*/
for(e=GL->adjList[gettop].firstedge; e; e=e->next)
{ /*對此頂點弧表遍歷*/
k = e->adjvex;
if(--GL->adjList[k].in == 0)  //將k號頂點鄰接點的入度減1,且減1後,入度為0的頂點需要存到棧中
stack[++top] = k;
if((etv[gettop]+e->weight)>etv[k]) /*求各頂點時間最早發生時間*/
etv[k] = etv[gettop] + e->weight;  /*某個頂點的最早發生時間= 和他相關的活動必須全部完成*/
}
}
if(count < GL->numVertexes)  /*如果count小於頂點數,說明存在環*/
return -1;
else
return 0;
}
/*求關鍵路徑,GL為有向網,輸出GL的各項關鍵活動*/
void CriticalPath(GraphAdjList GL)
{
EdgeNode *e;
int i,gettop,k,j;
int ete,lte;    /*聲明活動最早發生時間和最遲發生時間*/
TopoLogicalSort(GL);  /*求拓撲序列,計算數組etv和stack2的值*/
ltv = (int*) malloc(GL->numVertexes*sizeof(int)); /*時間的最晚發生時間*/
for(i= 0; i<GL->numVertexes;i++)
ltv[i]=etv[GL->numVertexes-1];  /*初始化ltv[i] 為工程完成的最早時間,etv[i]初始化為0*/
while(top2!=0)  /*計算ltv*/
{
gettop = stack2[top2--];
for(e=GL->adjList[gettop].firstedge;e!=NUll;e=e->next)
{/*求各定點事件的最遲發生時間ltv值*/
k=e->adjvex;
if(ltv[k]-e->weight<ltv[gettop])
ltv[gettop]= ltv[k]-e->weight;   /*求最晚發生時間,是從拓撲序列的最後一個頂點逆著推導*/
}
}
for(j=0;j<GL->numVertexes;j++)  /*求關鍵活動*/
{
for(e=GL->adjList[j].firstedge;e!=NULL;e=e->next)
{
k=e->adjvex;
ete = etv[j];   /*活動最早開始時間*/
lte = ltv[k] - e->weight;/*活動最晚發生時間*/
if(ete ==lte)
printf("<v%d,v%d> length: %d, ",GL->adjList[j].data,GL->adjList[k].data,e->weight);
}
}
}

 

本文出自 “李海川” 博客,請務必保留此出處http://lihaichuan.blog.51cto.com/498079/1293633

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved