1.圖的定義
1)線性表咱們把數據元素叫作元素,樹中將數據元素叫結點,在圖中數據元素,咱們則稱之爲頂點。
2)線性表中能夠沒有數據元素,稱爲空表。樹中能夠沒有結點,叫作空樹。再圖結構中,不容許沒有頂點。
3)線性表中,相鄰的數據元素之間具備線性關係,樹結構中,相鄰兩層的結點具備層次關係,而圖中兩個頂點之間均可能有關係,頂點
之間的邏輯關係來表示,邊集能夠是空的。
2.各類圖定義
無向邊:若頂點v1到v1之間的邊沒有方向,則稱這條邊爲無向邊,用無序偶對來表示。若是圖中任意兩個頂點之間的邊都是無向邊,則稱爲
無向圖。
有向邊:若從頂點vi到vj的邊有方向,則稱這條邊爲有向邊,也稱爲弧。
若是圖中任意兩個頂點之間的邊都是有向邊,則這個圖爲有向圖。
*看清楚,有向邊用小括號"()"表示,而有向邊則是用尖括號"<>"表示。
入度,出度
連通圖,強連通
圖按照有無方向分爲無向圖和有向圖。無向圖由頂點和邊構成,有向圖由頂點和弧構成,弧有弧頭和弧尾之分。
圖按照邊或弧的多少分爲稀疏圖和稠密圖。若是任意兩個頂點之間都存在邊叫徹底圖。有向的叫有向徹底圖。若無重複的邊或頂點到自身的邊則叫簡單圖。
圖中頂點之間有鄰接點。無向圖頂點的邊數叫作度,有向圖頂點分爲出度和入度。
圖上的邊或弧上帶權則稱爲網。
圖中頂點間存在路徑,兩頂點存在路徑則說明是連通,若是路徑最終回到起始點則稱爲環,當中不重複叫簡單路徑。若任意兩頂點都是連通的,則圖就是
連通圖,有向則稱爲強連通。
無向圖中連通且n個頂點n-1條邊叫生成樹。有向圖中一頂點入度爲0其他頂點入度爲1的叫有向圖。一個有向圖由若干棵有向樹構成生成森林。
2.圖的存儲結構
1)鄰接矩陣
考慮到圖是由頂點和邊或弧兩部分組成,合在一塊兒比較困難,那就很天然考慮到分兩個結構分別存儲。頂點不分大小、主次,須要一個二維數組來存儲。
圖的鄰接矩陣存儲方式是兩個數組來表示圖。一個一維數組存儲圖中頂點信息,一個二維數組(稱爲鄰接矩陣)存儲圖中的邊或弧的信息。
無向圖是的邊數組是一個對稱矩陣。
對稱矩陣
即從矩陣的左上角到右下角的主對角軸,右上角的元與左下角相對應的元所有都是相等的。
代碼實現:
typedef char VertexType;/* 頂點類型由用戶自定義 */
typedef int EdgeType; /* 邊上的權值類型應由用戶定義 */
#define MAXVEX 100 /* 最大頂點數,應由用戶定義*/
#define INFINITY 65535 /* 用65535表明無窮*/
typedef struct
{
VertexType vexs[MAXVEX]; /*頂點表*/
EdgeType arc[MAXVEX][MAXVEX];/*鄰接矩陣,可看作邊表 */
int numVertexes, numEdges;/* 圖中當前的 頂點數 和 邊數 */
}MGraph;
有了這個結構定義,咱們構造一個圖,其實就是給頂點表和邊表輸入數據的過程。咱們看看無向圖的建立代碼。
void CreateMGraph(MGraph *G)
{
int i,j,k,w;
printf("輸入頂點數和邊數:\n");
scanf("%d,%d",&G->numVertexes,&G->numEdges); /* 輸入頂點數和邊數 */
for(i=0;i<G->numVertexes;i++)/* 讀入頂點信息,創建頂點表*/
scanf("%c",&G->vexs[i]);
for(i=0;i<G->numVertexes;i++)
for(j=0;j<G->numVertexes;j++)
G->arc[i][j] = INFINITY;/*鄰接矩陣初始化*/
for(k=0;k<G->numEdges;k++) /** 讀入numEdges條邊,創建鄰接矩陣**/
{
printf("輸入邊(vi,vj)上的下標i,下標j和權w:\n");
scanf("%d,%d,%d",&i,&j,&w);/* 輸入邊(vi,vj)上的權w */
G->arc[i][j] = w;
G->arc[j][i] = G->arc[i][j];/* 由於是無向圖,矩陣對稱 */
}
}
從代碼也能夠獲得,n個頂點和e條邊的無向網圖的建立,時間複雜度爲O(n+n*n+e),其中對鄰接矩陣的初始化耗費了O(n*n)的時間。
2)鄰接表
鄰接矩陣是不錯的一種圖存儲結構,可是咱們發現,對於邊數相對頂點較少的圖,這種結構是存在對存儲空間的極大的浪費
數組與鏈表相結合的存儲方法稱爲連接表。
鄰接表的處理方法:
1.圖中頂點用一個一維數組存儲。
2.圖中每一個頂點vi的全部鄰接點構成一個線性表,因爲鄰接點的個數不定,因此用單鏈表存儲。
定義結構體:
typedef char VertexType; /* 頂點類型應由用戶定義*/
typedef int EdgeType; /* 邊上的權值類型應該由用戶定義*/
typedef struct EdageNode /* 邊表結點 */
{
int adjvex; /* 鄰邊點域,存儲該結點對應的下標*/
EdgeType weight;/* 用於存儲權值,對於非網圖能夠不須要*/
struct EdgeNode *next;/* 鏈域,指向下一個鄰接點 */
}EdgeNode;
typedef struct VertexNode /* 頂點表結點 */
{
VertexType data;/* 頂點域,存儲頂點信息 */
EdgeNode *firstedge; /* 邊表頭指針 */
}VertexNode,AdjList[MAXVEX];
typedef struct
{
AdjList adjList;
int numVertexes,numEdges; /* 圖中當前頂點數和邊數 */
}GraphAdjList;
無向圖的鄰接表建立代碼以下:(這裏沒有搞明白,研究一下頭插法)
void CreateALGraph(GraphAdjList *G)
{
int i,j,k;
EdgeNode *e;
printf("輸入頂點數和邊數:\n");
scanf("%d,%d",&G->numVertexes,&G->numEdges);/* 輸入頂點數和邊數 */
for(i=0;i<G->numVertexes;i++) /* 讀入頂點信息,創建頂點表 */
{
scanf(&G->adjList[i].data); /* 輸入頂點信息 */
G->adjList[i].firstedge = NULL; /* 將邊表置爲空表 */
}
for(k=0;k<G->numEdges;k++) /* 創建邊表*/
{
printf("輸入邊(vi,vj)上的頂點序號:\n");
scanf("%d,%d",&i,&j); /* 輸入邊(vi,vj)上的頂點序號 */
e = (EdgeNode *)malloc(sizeof(EdgeNode)); /* 向內存申請空間 */
/* 生成邊表結點 */
e->adjvex = j; /* 鄰接序號爲j */
e->next = G->adjList[i].firstedge; /* 將e指針指向當前頂點指向的結點 */
G->adjList[j].firstedge = e; /* 將當前頂點的指針指向 e */
e = (EdgeNode *)malloc(sizeof(EdgeNode)); /* 向內存申請空間 */
/* 生成邊表結點 */
e->adjvex = i; /* 鄰接序號爲j */
e->next = G->adjList[j].firstedge; /* 將e指針指向當前頂點指向的結點 */
G->adjList[j].firstedge = e; /* 將當前頂點的指針指向 e */
}
}
3)十字鏈表
十字鏈表的好處就是由於把鄰接表和逆鄰接表整合在了一塊兒,這樣既容易找到以vi爲尾的弧,也容易找到以vi爲頭的弧,因
而容易求得頂點的出度和入度。並且它除告終構複雜
一點外,其實建立圖算法的時間複雜度和鄰接表相同,所以,再有向圖中的
應用中,十字鏈表是很是好的數據結構模型。
4)鄰接多重表
鄰接多重表與鄰接表區別,僅僅是在於同一條表在鄰接表中用兩個結點表示,而在鄰接多重表中只有一個結點,這樣對邊的操做
就方便的多了,修改指向就能夠了。
5)邊集數組
邊集數組是由兩個一維數組構成,一個是存儲頂點的信息;另外一個是存儲邊的信息,這個邊數組每一個數據元素由一條邊的起點下標(begin)、
終點下標(end)和權(weight)組成。
------------------------
|begin | end | weight|
------------------------
其中begin是存儲起點下標,end是存儲終點下標,weight是存儲權值
3.圖的遍歷
從圖中某一頂點出發訪遍圖中其他頂點,且每個頂點僅被訪問一次,這個過程就要作圖的遍歷。
對於圖的遍從來說,如何避免因迴路陷入死循環,就須要科學地設計遍歷方案,一般由兩種遍歷次序方案:深度優先和廣度優先遍歷。
1)深度優先遍歷
深度優先遍歷(Depth_First_Search),也有稱爲深度優先搜索,簡稱DFS。
深度優先遍歷其實就是一個遞歸的過程。
若是咱們用的是鄰接矩陣的方式,則代碼以下:
typedef int Boolean; /* Boolean是布爾類型,其值是true或者false */
Boolean visitad[MAX]; /* 訪問標誌的數組 */
/* 鄰接矩陣的深度優先遞歸算法 */
void DFS(MGraph G,int i)
{
int j;
visitad[i] = TRUE;
printf("%c", G.vexs[i]); /* 打印頂點,也能夠其餘操做 */
for(j=0; j<G.numVertexes; j++)
if(G.arc[i][j] == 1 && !visited[j])
DFS(G,j); /* 對爲訪問的鄰接頂點遞歸調用 */
}
/* 鄰接矩陣的深度優先遍歷操做 */
void DFSTraverse(MGraph G)
{
int i;
for(i=0; i<G.numVertexes; i++)
visitad[i] = FALSE; /* 初始化全部頂點狀態都是未訪問過的狀態 */
for(i = 0;i<G.numVertexes; i++)
if(!visitad[i]) /* 對未訪問的頂點調用DFS,如果連通圖,只會執行一次 */
DFS(G,i);
}
鏈表實現方法:
/************ 鄰接表的深度優先遞歸算法 *****************/
void DFS(GraphAdjList GL, int i)
{
EdgeNode *p;
visited[i] = TRUE;
printf("%c",GL->adjList[i].data); /* 打印頂點,也能夠其餘操做 */
p = GL->adjList[i].firstedge;
while(p)
{
if(!visited[p->adjvex])
DFS(GL,p->adjvex);/* 對未訪問的鄰接頂點遞歸調用 */
p= p->next;
}
}
/** 鄰接表的深度遍歷操做 */
void DFSTraverse(GraphAdjList GL)
{
int i;
for(i =0;i< GL->numVertexes; i++)
visited[i] = FALSE; /** 初始化所欲偶頂點狀態都是未訪問過的狀態 **/
for(i =0;i<GL->numVertexes; i++)
if(!visited[i]) /* 對未訪問過的頂點調用DFS,如果連通圖,只會執行一次 */
DFS(GL,i);
}
2)廣度優先遍歷
廣度優先遍歷(Breadth_First_Search),又稱爲廣度優先搜索,簡稱BFS.
鄰接矩陣結構的代碼:
/* 鄰接矩陣的廣度遍歷算法 */
void BFSTraverse(MGraph G)
{
int i,j;
Queue Q;
for(i=0;i<G.numVertexes;i++)
visited[i] = FALSE;
InitQueue(&Q); /* 初始化一輔助用的隊列 */
for(i=0;i<G.numVertexes;i++)/* 對每個頂點作循環 */
{
if(!visited[i]) /* 如果未訪問過就處理 */
{
visited[i] = TRUE;/*設置當前頂點訪問過*/
printf("%c",G.vexs[i]); /* 打印頂點,也能夠其餘操做*/
EnQueue(&Q,i);/*將此頂點入隊列 */
while(!QueueEmpty(Q)) /*若當前隊列不爲空*/
{
DeQueue(&Q,&i); /* 將隊中元素出隊列,賦值給i */
for(j=0;j<G.numVertexes;j++)
{
/*判斷其餘頂點若與當前頂點存在邊且未訪問過 */
if(G.arc[i][j] == 1 && !visited[j])
{
visited[j] = TRUE; /* 將找到的此頂點標記爲已訪問 */
printf("%c ",G.vexs[j]); /* 打印頂點 */
EnQueue(&Q,j); /* 將找到的此頂點入隊列 */
}
}
}
}
}
}
鄰接表的廣度優先遍歷,代碼與鄰接矩陣差別不大,代碼以下:
/* 鄰接表的廣度遍歷算法 */
void BFSTraverse(GraphAdjList GL)
{
int i;
EdgeNode *p;
Queue Q;
for(i=0; i< GL->numVertexes; i++)
{
visited[i] = FALSE;
}
InitQueue(&Q);
for(i = 0;i<GL->numVertexes;i++)
{
if(!visited[i])
{
visited[i] = TRUE;
printf("%c",GL->adjList[i].data); /*打印頂點,也能夠其餘操做*/
EnQueue(&Q,i);
while(!QueueEmpty(Q))
{
DeQueue(&Q,&i);
p = GL->adjList[i].firstedge;/*找到當前頂點邊錶鏈表頭指針*/
while(p)
{
if(!visited[p->adjvex]) /*若此頂點未被訪問*/
{
visited[p->adjvex] = TRUE;
printf("%c ",GL->adjList[p->adjvex].data);
EnQueue(&Q,p->adjvex); /* 將此頂點入隊列*/
}
p = p->next; /* 指針指向下一個鄰接點*/
}
}
}
}
}
4.最小生成樹
構造連通網的最小代價生成樹稱爲最小生成樹。
找連通圖的最小生成樹,經典算法有兩種,普里姆算法和克魯斯卡爾算法。
1)普利姆(Prim)算法
/* Prim 算法最小生成樹 */
void MiniSpanTree_Prim(MGraph G)
{
int min,i,j,k;
int adjvex[MAXVEX];/* 保存相關頂點下標 */
int lowcost[MAXVEX]; /* 保存相關頂點間邊的權值 */
lowcost[0] = 0; /*初始化第一個權值爲0 ,即v0加入生成樹 */
/*lowcost 的值爲0,在這裏就是此下標的頂點已經加入生成樹*/
adjvex[0] = 0; /*初始化第一個頂點下標爲0*/
for(i=1;i<G.numVertexes; i++) /*循環除下標爲0外的所有結點*/
{
lowcost[i] = G.arc[0][i]; /*將v0頂點與之有邊的權值存入數組*/
adjvex[i] = 0; /* 初始化都爲v0的下標*/
}
for(i = 0;i<G.numVertexes; i++)
{
if(lowcost[j] != 0 && lowcost[j] < min)
{/*若是權值不爲0,且權值小於min */
min = lowcost[j]; /* 則讓當前權值成爲最小值 */
k = j;/*將當前權值成爲最小值*/
}
j++;
}
printf("(%d,%d)", adjvex[k],k); /*打印當前頂點邊中權值最小邊 */
lowcost[k] = 0; /* 將當前權值設置爲 0 ,表示此頂點已經完成任務 */
for(j = 1;j< G.numVertexes; j++)
{
if(lowcost[j] != 0 && G.arc[k][j] < lowcost[j])
{
/* 若下標爲k頂點各邊權值小於此前這些頂點未被加入生成樹權值*/
lowcost[j] = G.arc[k][j]; /*將較小的權值存入lowcost */
adjvex[j] = k; /* 將下標爲k的頂點存入adjvex*/
}
}
}
2)克魯斯卡爾(Kruskal)算法
普利姆(Prim)算法是以某頂點爲起點,逐步找各頂點上最小權值的邊來構建最小生成樹的。咱們能夠直接就以邊爲目標去構建,由於權值是在邊上,直接去找
最小權值的邊來構建生成最小樹。
edge邊集數組結構的定義代碼:
/*對邊集數組Edge結構的定義*/
typedef struct
{
int begin;
int end;
int weight;
}Edge;
克魯斯卡爾算法代碼以下,左側數字爲行號。其中MAXEDGE爲邊數量的極大值,此處大於等於15便可,MAXVEX爲頂點個數最大值,此處大於等於9便可。
實現代碼:
/* Kruskal 算法生成最小生成樹 */
void MiniSpanTree_Kruskal(MGraph G) /* 生成最小生成樹 */
{
int i,n,m;
Edge edges[MAXEDGE]; /*定義邊集數組 */
int parent[MAXVEX]; /*定義一數組用來判斷邊與邊是否造成環路 */
/* 此處省略將鄰接矩陣G轉化爲邊集數組edges並按由小到大排序的代碼*/
for(i = 0; i< G.numVertexes; i++)
parent[i] = 0; /*初始化數組值爲0 */
for(i=0;i<G.numEdges; i++) /*循環每一條邊 */
{
n = Find(parent,edges[i].begin);
m = Find(parent,edges[i].begin);
if(n != m) /*假如n與m不等,說明此邊沒有與現有生成樹造成環路 */
{
parent[n] = m; /* 將此邊的結尾頂點放入下標爲頂點的parent中*/
/* 表示此頂點已經在生成樹集合中*/
printf(" (%d, %d) %d ",edges[i].begin,edges[i].end,edges[i].weight);
}
}
}
int Find(int *parent, int f)/* 查找連線頂點的尾部下標 */
{
while(parent[f] > 0)
f = parent[f];
return f;
}
5.最短路徑
1)迪傑斯特拉(Dijkstra)算法
按路徑長度遞增的次序產生最短路徑的算法。
代碼實現:
#define MAXVEX 9
#define INFINITY 65535
typedef int Pathmatirx[MAXVEX]; /*用於存儲最短路徑下標的數組*/
typedef int ShortPathTable[MAXVEX]; /*用於存儲到各點最短路徑的權值和*/
/* Dijkstra算法,求有向網G的v0頂點到其他頂點v最短路徑P[v]及帶權長度D[v]*/
/* P[v]的值爲前驅頂點下標,D[v]表示v0到v的最短路徑長度和 */
void ShortestPath_Dijkstra(MGraph G, int v0, Pathmatirx *P,ShortPathTable *D)
{
int v,w,k,min;
int final[MAXVEX]; /* final[w] = 1 表示求得頂點v0至vw的最短路徑 */
for(v =0;v<G.numVertexes; v++)
{
final[v] = 0; /*所有頂點初始化爲未知最短路徑狀態 */
(*D)[v] = G.matirx[v0][v]; /* 將與v0點右連線的點加上權值 */
(*p)[v] = 0; /*初始化路徑數組p爲 0 */
}
(*D)[v0] = 0; /*v0至v0的路徑爲0 */
final[v0] = 1; /* v0 至v0不須要路徑 */
/* 開始主循環,每次求得v0到某個v頂點的最短路徑 */
for(v = 1; v < G.numVertexes; v++)
{
min = INFINITY; /*當前所知離v0頂點的最近距離*/
for(w = 0; w<G.numVertexes; w++) /*尋找離v0最近的頂點 */
{
if(!final[w] && (*D)[w]<min)
{
k = w;
min = (*D)[w]; /*w頂點離v0頂點最近*/
}
}
final[k] = 1; /* 將目前找到的最近的頂點置爲1 */
for(w= 0; w<G.numVertexes; w++) /*修正當前最短路徑及距離*/
{
/* 若是通過v頂點的路徑比如今這條路徑的長度短的話*/
if(!final[w] && (min + G.matirx[k][w] < (*D)[w]))
{
/*說明了找到了更短的路徑,修改D[w]和P[w] */
(*D)[w] = min + G.matirx[k][w]; /* 修改當前路徑長度 */
(*p)[w] = k;
}
}
}
}
2)弗洛伊德(Floyd)算法
這個算法涉及到了矩陣的知識,上代碼
typedef int Pathmatirx[MAXVEX][MAXVEX];
typedef int ShortPathTable[MAXVEX][MAXVEX];
/*Floyd算法,求網圖G中各頂點v到其他頂點w最短路徑P[v][w]及帶權長度D[v][w] */
void ShortestPath_Floyd(MGraph G, Pathmatirx *p, ShortPathTable *D)
{
int v,w,k;
for(v=0;v<G.numVertexes; ++v)/*初始化D與P*/
{
for(w=0;w<G.numVertexes; ++w)
{
(*D)[v][w] = G.matirx[v][w]; /* D[v][w]值即爲對應點間的權值 */
(*P) [v][w] = w; /*初始化P */
}
}
for(k = 0; k<G.numVertexes; ++k)
{
for(v = 0;v < G.numVertexes; ++v)
{
for(w = 0;w<G.numVertexes; ++w)
{
if((*D)[v][w] > (*D)[v][k] + (*D)[k][w])
{
/* 若是淨多下標爲k頂點路徑比原兩點間路徑更粗*/
/* 將當前兩點間權值設爲更小的一個 */
(*D)[v][w] = (*D)[v][w] + (*D)[k][w];
(*P)[v][w] = (*P)[v][k]; /*路徑設置淨多下標爲k的頂點*/
}
}
}
}
}
6.拓撲排序
在一個表示工程的有向圖中,用頂點表示活動,用弧表示活動之間的優先關係,這樣的有向圖爲頂點表示活動的網,咱們稱爲AOV網。
所謂拓撲排序,其實就是對一個有向圖構造拓撲序的過程。
1)拓撲排序算法
基本思路:
從AOV網中選擇一個入度爲0的頂點輸出,而後刪去此頂點,並刪除以此爲頂點爲尾的弧,繼續重複此步驟,直到輸出所有頂點
或者AOV網中不存在入度爲0的頂點爲止。
拓撲排序中,設計到的結構代碼以下:
typedef struct EdgeNode /*邊表結點*/
{
int adjvex; /* 鄰接點域,存儲該頂點對應的下標 */
int weight; /*用於存儲權值,對於非同圖能夠不須要 */
struct RdgeNode *next; /* 鏈域,指向下一個鄰接點 */
}EdgeNode;
typedef struct VertexNode /* 頂點表結點 */
{
int in; /* 頂點入度 */
int data; /* 頂點域,存儲頂點信息 */
EdgeNode *firstedge; /* 邊表頭指針 */
}VertexNode,AdjList[MAXVEX];
typedef struct
{
AdjList adjList;
int numVertexes,numEdges; /* 圖中當前頂點數和邊數 */
}graphAdjList, *GraphAdjList;
/* 拓撲排序,若GL無迴路,則輸出拓撲排序序列並返回OK,如有迴路則返回ERROR */
Status 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));
for(i = 0;i<GL->numVertexes; i++)
if(GL->adjList[i].in == 0)
stack[++top] = i; /* 將入度爲0 */
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))/*將k號頂點鄰接點的入度減爲1*/
stack[++top] = k; /* 若爲0則入棧,以便於下次循環輸出 */
}
}
if(count < GL->numVertexes) /* 若是count 小於頂點數 ,說明存在環 */
{
return ERROR;
}
else
return OK;
}
7.關鍵路徑
拓撲排序主要是爲解決一個工程可否順序進行的問題,但有時咱們還須要解決工程完成須要的最短期問題。
在AOV網的基礎上,咱們來介紹一個新的概念。在一個表示工程的帶權有向圖中,用頂點表示事件,用有向邊表示活動,用邊上的權值表示活動的持續時間,
這種有向圖的邊表示活動的網,咱們稱之爲AOE網。咱們把AOE網中沒有入邊的頂點稱爲始點或源點,沒有出邊的頂點稱爲終點或匯點。
須要幾個參數:
前兩個事件是預計:
1.事件的最先發生時間etv(earlist time of vertex):即頂點vk的最先發生時間。
2.事件的最晚發生時間ltv(latest time of vertex):即頂點vk的最晚發生時間,也就是每一個頂點對應的事件最晚須要開始的時間,超過此時間將會延誤整個工期。
這兩個事件開始作的時間預計:
3.活動的最先開工時間ete (earlist time of edge):即弧ak的最晚發生時間,也就是不推遲工期的最晚開工時間。
4.活動的最晚開工時間lte(latest time of edge):即弧ak的最晚發生時間,也就是不推遲工期的最晚開工時間。
關鍵路徑算法:
將AOE網轉化爲鄰接表結構,注意與拓撲排序時鄰接表結構不一樣的地方在於,這裏弧鏈表增長了weight域,用來存儲弧的權值。
首先聲明幾個全局變量:
int *etv,*ltv; /* 事件最先發生時間和最遲發生時間數組 */
int *stack2; /* 用於存儲拓撲序列的棧 */
int top2; /* 用於stack2的指針 */
/* 拓撲排序,用於關鍵路徑計算 */
Status TopologicalSort(GraphAdjList GL)
{
EdgeNode *e;
int i,k,gettop;
int top = 0; /* 用於棧指針下標 */
int count; /* 用於統計輸出頂點的個數 */
int *stack; /* 建棧將入度爲0的頂點入棧 */
stack = (int *)malloc(GL->numVertexes *sizeof(int));
for(i=0; i<GL->numVertexes; i++)
if(0 == GL->adjList[i].in)
stack[++top] = i;
top2 = 0; /*初始化爲0 */
etv = (int*)malloc(GL->numVertexes * sizeof(int)); /* 時間最先發生時間 */
for(i=0; i<GL->numVertexes; i++)
etv[i] = 0; /* 初始化爲0 */
stack2 = (int*)malloc(GL->numVertexes * sizeof(int)); /* 初始化 */
while(top != 0)
{
gettop = stack[top--];
count++;
stack2[++top2] = gettop; /* 將彈出的頂點序號壓入拓撲序列的棧 */
for(e = GL->adjList[gettop].firstedge;e;e = e->next)
{
k = e->adjvex;
if(!(--GL->adjList[k].in))
stack[++top] = k;
if((etv[gettop]+ e->weight)>etv[k]) /* 求各頂點事件最先發生時間值 */
etv[k] = etv[gettop] + e->weight;
}
}
if(count < GL->numVertexes)
return ERROR;
else
return OK;
}
算法