● 圖的存儲結構 數組
① //圖的數組 (鄰接矩陣 ) 存儲結構----有向圖, 無向圖, 網網絡 |
思路: 用兩個數組來表示圖 函數 ① 一個一維數組存儲圖中頂點信息; ② 一個二維數組(稱爲鄰接矩陣----是一個對稱矩陣,矩陣的元知足aij=aji)存儲圖中的邊或弧的信息。spa |
//起始部分 指針 #include "stdio.h" blog #include "stdlib.h" 排序 #include "io.h" 遞歸 #include "math.h" 隊列 #include "time.h"
#define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXVEX 100 /* 最大頂點數,應由用戶定義 */ #define INFINITY 65535 |
//用頂點數組和邊數組(鄰接矩陣)定義表結構 |
typedef int Status; /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */ typedef char VertexType; /* 頂點類型應由用戶定義 */ typedef int EdgeType; /* 邊上的權值類型應由用戶定義 */ typedef struct { VertexType vexs[MAXVEX]; /* 頂點表 */ EdgeType arc[MAXVEX][MAXVEX];/* 鄰接矩陣,可看做邊表, 這裏用edge這個標識符更準確 */ int numNodes, numEdges; /* 圖中當前的頂點數和邊數 */ }MGraph; |
/* 創建無向網圖的鄰接矩陣表示 */ |
void CreateMGraph(MGraph *G) { int i,j,k,w; printf("輸入頂點數和邊數:\n"); scanf("%d,%d",&G->numNodes,&G->numEdges); /* 輸入頂點數和邊數 */ for(i = 0;i <G->numNodes;i++) /* 讀入頂點信息,創建頂點表 */ scanf(&G->vexs[i]); for(i = 0;i <G->numNodes;i++) for(j = 0;j <G->numNodes;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]; /* 由於是無向圖,矩陣對稱 */ } } |
//主函數 |
int main(void) { MGraph Graph; CreateMGraph(&Graph);
return 0; }
對於有向圖, 咱們要先檢索鄰接矩陣的行, 再檢索列; 對於無向圖, 先檢索行或列均可以. |
② //鄰接表----有向圖, 無向圖, 網 |
//思路: ① 頂點用一維數組來儲存存儲數組, 這個數組是一個結構體數組, 每一個結構體數組的份量包括頂點的數據(data)和邊表頭結點的指針(firstedge), 指向該頂點的第一個鄰接點; ② 圖中每一個頂點的全部鄰接點構成一個鏈表, 邊界結點
※ 鄰接矩陣是惟一的(行列號與頂點編號一致),但鄰接表不惟一(連接次序與頂點編號無關)。 |
//鄰接表&逆鄰接表 鄰接表: 把頂點當弧尾(肯定頂點的出度)
逆鄰接表: 把頂點當弧頭(肯定頂點的入度) |
//起始部分 |
#include "stdio.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h"
#define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXVEX 100 /* 最大頂點數,應由用戶定義 */
typedef int Status; /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */ typedef char VertexType; /* 頂點類型應由用戶定義 */ typedef int EdgeType; /* 邊上的權值類型應由用戶定義 */ |
/* 邊 表 結點 */ |
typedef struct EdgeNode { int adjvex; /* 鄰接點域,存儲該頂點對應的下標 */ EdgeType info; /* 用於存儲權值,對於非網圖能夠不須要 */ struct EdgeNode *next; /* 鏈域,指向下一個鄰接點 */ }EdgeNode; |
/* 頂點 表 結點 */ |
typedef struct VertexNode { VertexType data; /* 頂點域,存儲頂點信息 */ EdgeNode *firstedge;/* 邊表頭指針 */ }VertexNode, AdjList[MAXVEX]; |
//整個鄰接表 |
typedef struct { AdjList adjList; int numNodes,numEdges; /* 圖中當前頂點數和邊數 */ }GraphAdjList; |
/* 創建圖的鄰接表結構 */ |
void CreateALGraph(GraphAdjList *G) { int i,j,k; EdgeNode *e; printf("輸入頂點數和邊數:\n"); scanf("%d,%d",&G->numNodes,&G->numEdges); /* 輸入頂點數和邊數 */ for(i = 0;i < G->numNodes;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[i].firstedge=e; /* 將當前頂點的指針指向e */
e=(EdgeNode *)malloc(sizeof(EdgeNode)); /* 向內存申請空間,生成邊表結點 */ e->adjvex=i; /* 鄰接序號爲i */ e->next=G->adjList[j].firstedge; /* 將e的指針指向當前頂點上指向的結點 */ G->adjList[j].firstedge=e; /* 將當前頂點的指針指向e */ } } |
//主函數 |
int main(void) { GraphAdjList Graph; CreateALGraph(&Graph);
return 0; } |
③ //十字鏈表----有向圖: 結合鄰接表和逆鄰接表 |
(從新定義頂點表結點結構和邊表結點結構) (1)頂點表結點,設3個域(每一個頂點也是一個數據元素)
data: 頂點信息 firstin: 以該頂點爲弧頭的第一條弧結點 firstout: 以該頂點爲弧尾的第一條弧結點
(2)邊表結點,設5個域(每段弧是一個數據元素)
tailvex: 弧尾頂點位置 headvex: 弧頭頂點位置 hlink: 指向和這條弧的弧頭相同的下一條弧的位置 tlink: 指向和這條弧的弧尾相同的下一弧弧的位置 info: 弧信息
|
④ //鄰接多重表----無向圖 |
(1)頂點表結點
data: 存儲頂點信息 firstedge: 指向依附於該頂點的第一條邊結點
(2)邊表結點
mark: 標誌域,標記該邊是否被搜索過。 ivex, jvex : 該邊依附的兩個頂點的位置 ilink: 指向下一條依附頂點 ivex 的邊 Jlink: 指向下一條依附頂點 jvex 的邊 info: 邊信息 |
⑤ //邊集數組----有向圖 邊集數組是由兩個一維數組構成。一個是存儲頂點的信息;另外一個是存儲邊的信息,這個邊數組每一個數據元素由一條邊的起點下標(begin)、終點下標(end)和權(weight)組成. 邊數組
|
● 深度優先遍歷(DFS)----前序遍歷
找鑰匙: ①從任意一個房間開始, 翻個底朝天; ②換到下一間, 再翻個底朝天...... 右手原則: 在沒有碰到重複頂點的狀況下, 分叉路口始終是向右手邊走.(固然也能夠遵循"左手原則"來遍歷) |
● 廣度優先遍歷(BFS)----前序遍歷
找鑰匙: ①查看全部房間裏最顯眼的位置; ②查看全部房間裏此顯眼的位置...... (深度優先遍歷和廣度優先遍歷的路徑都不惟一) |
● 鄰接矩陣----深度和廣度遍歷
//起始部分 |
#include "stdio.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h"
#define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0
typedef int Status; /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */ typedef int Boolean; /* Boolean是布爾類型,其值是TRUE或FALSE */
typedef char VertexType; /* 頂點類型應由用戶定義 */ typedef int EdgeType; /* 邊上的權值類型應由用戶定義 */
#define MAXSIZE 9 /* 存儲空間初始分配量 */ #define MAXEDGE 15 #define MAXVEX 9 #define INFINITY 65535 |
//圖的結構定義----用鄰接矩陣 |
typedef struct { VertexType vexs[MAXVEX]; /* 頂點表 */ EdgeType arc[MAXVEX][MAXVEX];/* 鄰接矩陣,可看做邊表 */ int numVertexes, numEdges; /* 圖中當前的頂點數和邊數 */ }MGraph; |
/* 用到的隊列結構與函數 */ /* 循環隊列的順序存儲結構 */ |
typedef struct { int data[MAXSIZE]; int front; /* 隊頭指針 */ int rear; /* */ }Queue; |
/* 初始化一個空隊列Q */ |
Status InitQueue(Queue *Q) { Q->front=0; Q->rear=0; return OK; } |
/* 若隊列Q爲空隊列,則返回TRUE,不然返回FALSE */ |
Status QueueEmpty(Queue Q) { if(Q.front==Q.rear) /* 隊列空的標誌 */ return TRUE; else return FALSE; } |
/* 若隊列未滿,則插入元素e爲Q新的隊尾元素 */ |
Status EnQueue(Queue *Q,int e) { if ((Q->rear+1)%MAXSIZE == Q->front) /* 隊列滿的判斷 */ return ERROR; Q->data[Q->rear]=e; /* 將元素e賦值給隊尾 */ Q->rear=(Q->rear+1)%MAXSIZE;/* rear指針向後移一位置, */ /* 若到最後則轉到數組頭部 */ return OK; } |
/* 若隊列不空,則刪除Q中隊頭元素,用e返回其值 */ |
Status DeQueue(Queue *Q,int *e) { if (Q->front == Q->rear) /* 隊列空的判斷 */ return ERROR; *e=Q->data[Q->front]; /* 將隊頭元素賦值給e */ Q->front=(Q->front+1)%MAXSIZE; /* front指針向後移一位置, */ /* 若到最後則轉到數組頭部 */ return OK; } |
//建立圖的函數 |
void CreateMGraph(MGraph *G) { int i, j;
G->numEdges=15; G->numVertexes=9;
/* 讀入頂點信息,創建頂點表 */ G->vexs[0]='A'; G->vexs[1]='B'; G->vexs[2]='C'; G->vexs[3]='D'; G->vexs[4]='E'; G->vexs[5]='F'; G->vexs[6]='G'; G->vexs[7]='H'; G->vexs[8]='I';
/* 初始化圖, 一行一行地將各元素初始化爲0 */ for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */ { for ( j = 0; j < G->numVertexes; j++) { G->arc[i][j]=0; } }
//將結點之間有邊的數組元素從新賦值, 下面構造的是對稱矩陣的左半部分 G->arc[0][1]=1; G->arc[0][5]=1;
G->arc[1][2]=1; G->arc[1][8]=1; G->arc[1][6]=1;
G->arc[2][3]=1; G->arc[2][8]=1;
G->arc[3][4]=1; G->arc[3][7]=1; G->arc[3][6]=1; G->arc[3][8]=1;
G->arc[4][5]=1; G->arc[4][7]=1;
G->arc[5][6]=1;
G->arc[6][7]=1;
//下面的操做是爲了造成一個對稱矩陣, 下面構造的是對稱矩陣的右半部分 for(i = 0; i < G->numVertexes; i++) { for(j = i; j < G->numVertexes; j++) { G->arc[j][i] =G->arc[i][j]; } }
} |
/* 鄰接矩陣的深度優先遞歸算法 */ |
Boolean visited[MAXVEX]; /* 訪問標誌的數組, 類型爲Boolean, 包括TRUE和FALSE兩個值 */
void DFS(MGraph G, int i) //DFS會被自身和DFSTraverse()函數調用, i是遍歷開始的頂點序號 { visited[i] = TRUE; //說明當前頂點已被訪問 printf("%c ", G.vexs[i]);/* 打印頂點,也能夠是其它操做 */ int j; for(j = 0; j < G.numVertexes; j++) if(G.arc[i][j] == 1 && !visited[j]) // visited[j]==0 DFS(G, j);/* 對爲訪問的鄰接頂點遞歸調用 */ } //在本程序中, 會先執行下面的DFSTraverse()函數, 此時i=0, 而後DFSTraverse()函數會調用DFS()函數, 此時, 從0號頂點開始遍歷, 而後DFS會遞歸調用自身, 每遍歷一個頂點, visited[i] = TRUE;這一句都會執行, 代表該頂點已被訪問. |
/* 鄰接矩陣的深度遍歷操做 */ |
void DFSTraverse(MGraph G) { int i; for(i = 0; i < G.numVertexes; i++) visited[i] = FALSE; /* 初始全部頂點狀態都是未訪問過狀態 */ for(i = 0; i < G.numVertexes; i++) if(!visited[i]) /* 對未訪問過的頂點調用DFS,如果連通圖,只會執行一次 */ DFS(G, i); } |
/* 鄰接矩陣的廣度遍歷算法 */ |
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]) //若是 G.arc[i][j]這條邊存在 { visited[j]=TRUE; /* 將找到的此頂點標記爲已訪問 */ printf("%c ", G.vexs[j]); /* 打印頂點 */ EnQueue(&Q,j); /* 將找到的此頂點入隊列 */ } } } } } } |
//主函數 |
int main(void) { MGraph Graph; CreateMGraph(&Graph); printf("\n深度遍歷:"); DFSTraverse(Graph); printf("\n廣度遍歷:"); BFSTraverse(Graph); return 0; } |
● 鄰接表----深度和廣度遍歷
//起始部分 |
#include "stdio.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h"
#define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0
#define MAXSIZE 9 /* 存儲空間初始分配量 */ #define MAXEDGE 15 #define MAXVEX 9 #define INFINITY 65535
typedef int Status; /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */ typedef int Boolean; /* Boolean是布爾類型,其值是TRUE或FALSE */
typedef char VertexType; /* 頂點類型應由用戶定義 */ typedef int EdgeType; /* 邊上的權值類型應由用戶定義 */ |
/* 鄰接矩陣結構 */ |
typedef struct { VertexType vexs[MAXVEX]; /* 頂點表 */ EdgeType arc[MAXVEX][MAXVEX];/* 鄰接矩陣,可看做邊表 */ int numVertexes, numEdges; /* 圖中當前的頂點數和邊數 */ }MGraph; |
/* 鄰接表結構 */ |
typedef struct EdgeNode /* 邊表結點 */ { int adjvex; /* 鄰接點域,存儲該頂點對應的下標 */ int weight; /* 用於存儲權值,對於非網圖能夠不須要 */ struct EdgeNode *next; /* 鏈域,指向下一個鄰接點 */ }EdgeNode; |
/* 頂點表結點 */ |
typedef struct VertexNode { int in; /* 頂點入度 */ char data; /* 頂點域,存儲頂點信息 */ EdgeNode *firstedge;/* 邊表頭指針 */ }VertexNode, AdjList[MAXVEX]; |
/* 邊表結點 */ |
typedef struct { AdjList adjList; int numVertexes,numEdges; /* 圖中當前頂點數和邊數 */ }graphAdjList,*GraphAdjList; /* **************************** */ |
/* 用到的隊列結構與函數 */ /* 循環隊列的順序存儲結構 */ |
typedef struct { int data[MAXSIZE]; int front; /* 頭指針 */ int rear; /* 尾指針,若隊列不空,指向隊列尾元素的下一個位置 */ }Queue; |
/* 初始化一個空隊列Q */ |
Status InitQueue(Queue *Q) { Q->front=0; Q->rear=0; return OK; } |
/* 若隊列Q爲空隊列,則返回TRUE,不然返回FALSE */ |
Status QueueEmpty(Queue Q) { if(Q.front==Q.rear) /* 隊列空的標誌 */ return TRUE; else return FALSE; } |
/* 若隊列未滿,則插入元素e爲Q新的隊尾元素 */ |
Status EnQueue(Queue *Q,int e) { if ((Q->rear+1)%MAXSIZE == Q->front) /* 隊列滿的判斷 */ return ERROR; Q->data[Q->rear]=e; /* 將元素e賦值給隊尾 */ Q->rear=(Q->rear+1)%MAXSIZE;/* rear指針向後移一位置, */ /* 若到最後則轉到數組頭部 */ return OK; } |
/* 若隊列不空,則刪除Q中隊頭元素,用e返回其值 */ |
Status DeQueue(Queue *Q,int *e) { if (Q->front == Q->rear) /* 隊列空的判斷 */ return ERROR; *e=Q->data[Q->front]; /* 將隊頭元素賦值給e */ Q->front=(Q->front+1)%MAXSIZE; /* front指針向後移一位置, */ /* 若到最後則轉到數組頭部 */ return OK; } |
//建立圖的函數 |
void CreateMGraph(MGraph *G) { int i, j;
G->numEdges=15; G->numVertexes=9;
/* 讀入頂點信息,創建頂點表 */ G->vexs[0]='A'; G->vexs[1]='B'; G->vexs[2]='C'; G->vexs[3]='D'; G->vexs[4]='E'; G->vexs[5]='F'; G->vexs[6]='G'; G->vexs[7]='H'; G->vexs[8]='I';
for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */ { for ( j = 0; j < G->numVertexes; j++) { G->arc[i][j]=0; } }
G->arc[0][1]=1; G->arc[0][5]=1;
G->arc[1][2]=1; G->arc[1][8]=1; G->arc[1][6]=1;
G->arc[2][3]=1; G->arc[2][8]=1;
G->arc[3][4]=1; G->arc[3][7]=1; G->arc[3][6]=1; G->arc[3][8]=1;
G->arc[4][5]=1; G->arc[4][7]=1;
G->arc[5][6]=1;
G->arc[6][7]=1;
for(i = 0; i < G->numVertexes; i++) { for(j = i; j < G->numVertexes; j++) { G->arc[j][i] =G->arc[i][j]; } }
} |
/* 利用鄰接矩陣構建鄰接表 */ |
void CreateALGraph(MGraph G,GraphAdjList *GL) { int i,j; EdgeNode *e;
*GL = (GraphAdjList)malloc(sizeof(graphAdjList));
(*GL)->numVertexes=G.numVertexes; (*GL)->numEdges=G.numEdges; for(i= 0;i <G.numVertexes;i++) /* 讀入頂點信息,創建頂點表 */ { (*GL)->adjList[i].in=0; (*GL)->adjList[i].data=G.vexs[i]; (*GL)->adjList[i].firstedge=NULL; /* 將邊表置爲空表 */ }
for(i=0;i<G.numVertexes;i++) /* 創建邊表 */ { for(j=0;j<G.numVertexes;j++) { if (G.arc[i][j]==1) { e=(EdgeNode *)malloc(sizeof(EdgeNode)); e->adjvex=j; /* 鄰接序號爲j */ e->next=(*GL)->adjList[i].firstedge; /* 將當前頂點上的指向的結點指針賦值給e */ (*GL)->adjList[i].firstedge=e; /* 將當前頂點的指針指向e */ (*GL)->adjList[j].in++;
} } }
} |
/* 鄰接表的深度優先遞歸算法 */ |
Boolean visited[MAXSIZE]; /* 訪問標誌的數組 */
void DFS(GraphAdjList GL, int i) { visited[i] = TRUE; printf("%c ",GL->adjList[i].data);/* 打印頂點,也能夠其它操做 */ EdgeNode *p; p = GL->adjList[i].firstedge; while(p) // while(p!=0) { 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); } |
/* 鄰接表的廣度遍歷算法 */ |
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; /* 指針指向下一個鄰接點 */ } } } } } |
//主函數 |
int main(void) { MGraph Graph; GraphAdjList GL; CreateMGraph(&Graph); CreateALGraph(Graph,&GL);
printf("\n深度遍歷:"); DFSTraverse(GL); printf("\n廣度遍歷:"); BFSTraverse(GL); return 0; }
|
● 最小生成樹—普里姆(Prim)算法
Prim算法特色(對頂點操做): 頂點歸併,與邊數無關,適於稠密網。 普里姆算法的基本思想: 在生成樹的構造過程當中,圖中 n 個頂點分屬兩個集合:已落在生成樹上的頂點集 U (其序號爲下面代碼中的數組int adjvex[MAXVEX]的元素)和還沒有落在生成樹上的頂點集V-U ,則應在全部連通U中的頂點和V-U中的頂點的邊中選取權值最小的邊(該邊的權值爲下面代碼中的數組int lowcost[MAXVEX]的元素)。 步驟: 設N=(V,{E})是連通網,TE是N上最小生成樹中邊的集合: (1)初始令U={u0},(u0屬於V), TE=NULL (2)在全部u屬於U,v屬於V-U的邊(u,v)屬於E中,找一條代價最小的邊(u0,v0) (3)將(u0,v0)併入集合TE,同時v0併入U (4)重複上述操做直至U=V爲止,則T=(V,{TE})爲N的最小生成樹 |
① 初始化兩個數組,一個用來存放邊的起始點,一個用於存放邊權值 ② 賦初值,從0開始,全部邊的權值都和v0相關,全部邊權值的起始點都是v0 ③.大循環1到MG.numVertexes-1次 ④ 遍歷邊權值數組,找當前數組中的最小值,並將發現的能構成最小權值的頂點的下標存入k, 以待使用 ⑤ 打印邊 ⑥ 將加入的頂點的lowcost設置爲0 ⑦ 使用新加入的頂點的鄰接矩陣來更新lowcost數組和adjvex數組 //起始部分 |
#include "stdio.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h"
#define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0
#define MAXEDGE 20 #define MAXVEX 20 #define INFINITY 65535
typedef int Status; /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */ |
//圖的結構定義 |
typedef struct { int arc[MAXVEX][MAXVEX]; int numVertexes, numEdges; }MGraph; |
/* 構件圖 */ |
void CreateMGraph(MGraph *G) { int i, j;
/* printf("請輸入邊數和頂點數:"); */ G->numEdges=15; G->numVertexes=9;
for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */ { for ( j = 0; j < G->numVertexes; j++) { if (i==j) G->arc[i][j]=0; else G->arc[i][j] = G->arc[j][i] = INFINITY; } }
G->arc[0][1]=10; G->arc[0][5]=11; G->arc[1][2]=18; G->arc[1][8]=12; G->arc[1][6]=16; G->arc[2][8]=8; G->arc[2][3]=22; G->arc[3][8]=21; G->arc[3][6]=24; G->arc[3][7]=16; G->arc[3][4]=20; G->arc[4][7]=7; G->arc[4][5]=26; G->arc[5][6]=17; G->arc[6][7]=19;
for(i = 0; i < G->numVertexes; i++) { for(j = i; j < G->numVertexes; j++) { G->arc[j][i] =G->arc[i][j]; } }
} |
/* Prim算法生成最小生成樹 */ |
void MiniSpanTree_Prim(MGraph G) { int min, i, j, k; int adjvex[MAXVEX]; /*已落在生成樹上的頂點集 U中各頂點的序號 */ int lowcost[MAXVEX];/*全部連通U中的頂點和V-U中的頂點的邊中選取權值最小的邊的權值*/
adjvex[0] = 0; /* 初始化第一個頂點下標爲0 */ lowcost[0] = 0; /* lowcost的值爲0, 由於一個頂點到其自身的權值爲0 */
for(i = 1; i < G.numVertexes; i++) /* 循環除下標爲0之外的所有頂點 */ { adjvex[i] = 0; /* 暫時將U中各頂點的序號初始化爲0 */ lowcost[i] = G.arc[0][i]; /* 將鄰接矩陣第0行全部權值先加入數組 */ } for(i = 1; i < G.numVertexes; i++) //注意, 下面有兩個內循環 { min = INFINITY; /*初始化最小權值爲∞, 一般設置爲不可能的大數字如3276七、65535等 */ j = 1; k = 0; while(j < G.numVertexes) /* 循環所有頂點 */ {//找出lowcost數組已存儲的最小權值 if(lowcost[j]!=0 && lowcost[j] < min)/* 若是權值不爲0且權值小於min */ { min = lowcost[j]; /* 則讓當前權值成爲最小值 */ k = j; /* 將發現的能構成最小權值的頂點的下標存入k, 以待使用 */ } j++; } printf("(%d, %d)\n", 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 */ } } } } |
//主函數 |
int main(void) { MGraph G; CreateMGraph(&G); MiniSpanTree_Prim(G);
return 0; }
|
咱們也能夠設置一個輔助的結構體數組closedge: struct { VertexType adjvex; // 已落在生成樹上的頂點集 U中各頂點的序號 VRType lowcost; // 全部連通U中的頂點和V-U中的頂點的邊中選取權值最小的邊的權值 } closedge[MAX_VERTEX_NUM]; |
初始狀態:V是全部頂點的集合,即V={A,B,C,D,E,F,G};U和T都是空! 第1步:將頂點A加入到U中。 此時,U={A}。 第2步:將頂點B加入到U中。 上一步操做以後,U={A}, V-U={B,C,D,E,F,G};所以,邊(A,B)的權值最小。將頂點B添加到U中;此時,U={A,B}。 第3步:將頂點F加入到U中。 上一步操做以後,U={A,B}, V-U={C,D,E,F,G};所以,邊(B,F)的權值最小。將頂點F添加到U中;此時,U={A,B,F}。 第4步:將頂點E加入到U中。 上一步操做以後,U={A,B,F}, V-U={C,D,E,G};所以,邊(F,E)的權值最小。將頂點E添加到U中;此時,U={A,B,F,E}。 第5步:將頂點D加入到U中。 上一步操做以後,U={A,B,F,E}, V-U={C,D,G};所以,邊(E,D)的權值最小。將頂點D添加到U中;此時,U={A,B,F,E,D}。 第6步:將頂點C加入到U中。 上一步操做以後,U={A,B,F,E,D}, V-U={C,G};所以,邊(D,C)的權值最小。將頂點C添加到U中;此時,U={A,B,F,E,D,C}。 第7步:將頂點G加入到U中。 上一步操做以後,U={A,B,F,E,D,C}, V-U={G};所以,邊(F,G)的權值最小。將頂點G添加到U中;此時,U=V。
此時,最小生成樹構造完成!它包括的頂點依次是:A B F E D C G。 |
● 最小生成樹----克魯斯卡爾(Kruskal)算法
Kruskal算法特色(對邊操做):邊歸併,適於求稀疏網。 克魯斯卡爾算法的基本思想: 考慮問題的出發點: 爲使生成樹上邊的權值之和達到最小,則應使生成樹中每一條邊的權值儘量地小。 能夠看出Kruskal算法屬於一種貪心算法,並且能保證找到全局最優解。 步驟: 設連通網N=(V,{E}),令最小生成樹 (1)初始狀態爲只有n個頂點而無邊的非連通圖T=(V,{NULL})--森林,每一個頂點自成一個連通份量 (2)在E中選取代價最小的邊,若該邊的兩個頂點不在同一個圖中(即沒有造成迴路),則將此邊加入到T中;不然,捨去此邊,選取下一條代價最小的邊 (3)依此類推,直至T中全部頂點都在同一連通份量上爲止
圖中先將每一個頂點看做獨立的子圖,而後查找最小權值邊,這條邊是有限制條件的,邊的兩個頂點必須不在同一個圖中(不然造成迴路, 這就不是連通圖的極小連通子圖(即生成樹)了.),如上圖,第一個圖中找到最小權值邊爲(v1,v3),且知足限制條件,繼續查找到邊(v4,v6),(v2,v5),(v3,v6),當查找到最後一條邊時,僅僅只有(v2,v3)知足限制條件,其餘的如(v3,v4),(v1,v4)都在一個子圖裏面,不知足條件,至此已經找到最小生成樹的全部邊。
|
① 定義一個邊集,一個頂點集, 邊集按照權的大小排列,將結點集所有初始化爲0 ② 循環邊集中的每一條邊, 用定義的Find()函數檢查每條邊的起點和終點能走到的最遠的結點在哪裏,若是相同就繼續循環,不然打印並修改parent數組
//起始部分 |
#include "stdio.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h"
#define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0
typedef int Status; /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */
#define MAXEDGE 20 #define MAXVEX 20 #define INFINITY 65535 |
//圖的結構 |
typedef struct { int arc[MAXVEX][MAXVEX]; int numVertexes, numEdges; }MGraph; |
/* 對邊集數組Edge結構的定義 */ |
typedef struct { int begin; int end; int weight; }Edge; |
/* 構件圖 */ |
void CreateMGraph(MGraph *G) { int i, j;
/* printf("請輸入邊數和頂點數:"); */ G->numEdges=15; G->numVertexes=9;
for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */ { for ( j = 0; j < G->numVertexes; j++) { if (i==j) G->arc[i][j]=0; else G->arc[i][j] = G->arc[j][i] = INFINITY; } }
G->arc[0][1]=10; G->arc[0][5]=11; G->arc[1][2]=18; G->arc[1][8]=12; G->arc[1][6]=16; G->arc[2][8]=8; G->arc[2][3]=22; G->arc[3][8]=21; G->arc[3][6]=24; G->arc[3][7]=16; G->arc[3][4]=20; G->arc[4][7]=7; G->arc[4][5]=26; G->arc[5][6]=17; G->arc[6][7]=19;
for(i = 0; i < G->numVertexes; i++) { for(j = i; j < G->numVertexes; j++) { G->arc[j][i] =G->arc[i][j]; } }
} |
/* 交換權值 以及頭和尾 */ |
void Swapn(Edge *edges,int i, int j) { int temp; temp = edges[i].begin; edges[i].begin = edges[j].begin; edges[j].begin = temp; temp = edges[i].end; edges[i].end = edges[j].end; edges[j].end = temp; temp = edges[i].weight; edges[i].weight = edges[j].weight; edges[j].weight = temp; } |
/* 對權值進行排序 */ |
void sort(Edge edges[],MGraph *G) { int i, j; for ( i = 0; i < G->numEdges; i++) { for ( j = i + 1; j < G->numEdges; j++) { if (edges[i].weight > edges[j].weight) { Swapn(edges, i, j); } } } printf("權排序以後的爲:\n"); for (i = 0; i < G->numEdges; i++) { printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight); }
} |
/* 查找連線頂點的尾部下標 */ |
int Find(int *parent, int f) //查找從f這個頂點開始能走到的最遠的頂點是哪裏 {//若是最後走到的最遠的頂點相同,說明是有環路生成的 while ( parent[f] > 0) { f = parent[f]; } return f; } |
/* 生成最小生成樹 */ |
void MiniSpanTree_Kruskal(MGraph G) { int i, j, n, m; int k = 0;
Edge edges[MAXEDGE];/* 定義邊集數組, 它是一個結構體數組*/ int parent[MAXVEX];/* 定義一數組用來判斷邊與邊是否造成環路 */
/* 用來構建邊集數組並排序********************* */ for ( i = 0; i < G.numVertexes-1; i++) { for (j = i + 1; j < G.numVertexes; j++) { if (G.arc[i][j]<INFINITY) { edges[k].begin = i; edges[k].end = j; edges[k].weight = G.arc[i][j]; k++; } } } sort(edges, &G); /* ******************************************* */
for (i = 0; i < G.numVertexes; i++) parent[i] = 0; /* 初始化數組值爲0 */
printf("打印最小生成樹:\n"); for (i = 0; i < G.numEdges; i++) /* 循環每一條邊 */ { n = Find(parent,edges[i].begin); m = Find(parent,edges[i].end); if (n != m) /* 假如n與m不等,說明此邊沒有與現有的生成樹造成環路 */ { parent[n] = m; // parent數組下標記錄了邊起點,數組元素記錄了邊終點 printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight); } } } |
//主函數 |
int main(void) { MGraph G; CreateMGraph(&G); MiniSpanTree_Kruskal(G); return 0; } |
用數組R保存最小生成樹結果
第1步:將邊<E,F>加入R中。 邊<E,F>的權值最小,所以將它加入到最小生成樹結果R中。 第2步:將邊<C,D>加入R中。 上一步操做以後,邊<C,D>的權值最小,所以將它加入到最小生成樹結果R中。 第3步:將邊<D,E>加入R中。 上一步操做以後,邊<D,E>的權值最小,所以將它加入到最小生成樹結果R中。 第4步:將邊<B,F>加入R中。 上一步操做以後,邊<C,E>的權值最小,但<C,E>會和已有的邊構成迴路;所以,跳過邊<C,E>。同理,跳過邊<C,F>。將邊<B,F>加入到最小生成樹結果R中。 第5步:將邊<E,G>加入R中。 上一步操做以後,邊<E,G>的權值最小,所以將它加入到最小生成樹結果R中。 第6步:將邊<A,B>加入R中。 上一步操做以後,邊<F,G>的權值最小,但<F,G>會和已有的邊構成迴路;所以,跳過邊<F,G>。同理,跳過邊<B,C>。將邊<A,B>加入到最小生成樹結果R中。
此時,最小生成樹構造完成!它包括的邊依次是:<E,F> <C,D> <D,E> <B,F> <E,G> <A,B>。 |
單源最短路徑(一頂點到其他各頂點)——用Dijkstra(迪傑斯特拉)算法 全部頂點間的最短路徑(任意兩頂點之間)——用Floyd(弗洛伊德)算法 |
● 最短路徑—Dijkstra算法
步驟: (1) 初始時,S只包含起點s;U包含除s外的其餘頂點,且U中頂點的距離爲"起點s到該頂點的距離"[例如,U中頂點v的距離爲(s,v)的長度,而後s和v不相鄰,則v的距離爲∞]。 (2) 從U中選出"距離最短的頂點k",並將頂點k加入到S中;同時,從U中移除頂點k。 (3) 更新U中各個頂點到起點s的距離。之因此更新U中頂點的距離,是因爲上一步中肯定了k是求出最短路徑的頂點,從而能夠利用k來更新其它頂點的距離;例如,(s,v)的距離可能大於(s,k)+(k,v)的距離。 (4) 重複步驟(2)和(3),直到遍歷完全部頂點。
初始狀態:S是已計算出最短路徑的頂點集合,U是未計算除最短路徑的頂點的集合! 第1步:將頂點D加入到S中。 此時,S={D(0)}, U={A(∞),B(∞),C(3),E(4),F(∞),G(∞)}。 注:C(3)表示C到起點D的距離是3。 第2步:將頂點C加入到S中。 上一步操做以後,U中頂點C到起點D的距離最短;所以,將C加入到S中,同時更新U中頂點的距離。以頂點F爲例,以前F到D的距離爲∞;可是將C加入到S以後,F到D的距離爲9=(F,C)+(C,D)。 此時,S={D(0),C(3)}, U={A(∞),B(23),E(4),F(9),G(∞)}。 第3步:將頂點E加入到S中。 上一步操做以後,U中頂點E到起點D的距離最短;所以,將E加入到S中,同時更新U中頂點的距離。仍是以頂點F爲例,以前F到D的距離爲9;可是將E加入到S以後,F到D的距離爲6=(F,E)+(E,D)。 此時,S={D(0),C(3),E(4)}, U={A(∞),B(23),F(6),G(12)}。 第4步:將頂點F加入到S中。 此時,S={D(0),C(3),E(4),F(6)}, U={A(22),B(13),G(12)}。
第5步:將頂點G加入到S中。 此時,S={D(0),C(3),E(4),F(6),G(12)}, U={A(22),B(13)}。 第6步:將頂點B加入到S中。 此時,S={D(0),C(3),E(4),F(6),G(12),B(13)}, U={A(22)}。 第7步:將頂點A加入到S中。 此時,S={D(0),C(3),E(4),F(6),G(12),B(13),A(22)}。
此時,起點D到各個頂點的最短距離就計算出來了:A(22) B(13) C(3) D(0) E(4) F(6) G(12)。 |
//起始部分 |
#include "stdio.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h"
#define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0
#define MAXEDGE 20 #define MAXVEX 20 #define INFINITY 65535
typedef int Status; /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */ |
typedef struct { int vexs[MAXVEX]; int arc[MAXVEX][MAXVEX]; int numVertexes, numEdges; }MGraph;
typedef int Patharc[MAXVEX]; /* 數組元素爲最短路徑下標 */ typedef int ShortPathTable[MAXVEX];/* 數組元素爲到各點的最短路徑的權值和 */ |
/* 構件圖 */ |
void CreateMGraph(MGraph *G) { int i, j;
/* printf("請輸入邊數和頂點數:"); */ G->numEdges=16; G->numVertexes=9;
for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */ { G->vexs[i]=i; }
for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */ { for ( j = 0; j < G->numVertexes; j++) { if (i==j) G->arc[i][j]=0; else G->arc[i][j] = G->arc[j][i] = INFINITY; } }
G->arc[0][1]=1; G->arc[0][2]=5; G->arc[1][2]=3; G->arc[1][3]=7; G->arc[1][4]=5;
G->arc[2][4]=1; G->arc[2][5]=7; G->arc[3][4]=2; G->arc[3][6]=3; G->arc[4][5]=3;
G->arc[4][6]=6; G->arc[4][7]=9; G->arc[5][7]=5; G->arc[6][7]=2; G->arc[6][8]=7;
G->arc[7][8]=4;
for(i = 0; i < G->numVertexes; i++) { for(j = i; j < G->numVertexes; j++) { G->arc[j][i] =G->arc[i][j]; } }
} |
/* Dijkstra算法,求有向網G的v0頂點到其他頂點v的最短路徑P[v]及帶權長度D[v] */ /* P[v]的值爲前驅頂點下標,D[v]表示v0到v的最短路徑長度和 */ |
void ShortestPath_Dijkstra(MGraph G, int v0, Patharc *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.arc[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++) //遍歷數組D,尋找與v0最近的頂點,已經找到路徑的頂點final=1就不須要再參與了 */ { if(!final[w] && (*D)[w]<min) { k=w; //找到後k存儲這個最近點的下標 min = (*D)[w]; //找到後min存儲這個最短路徑的權值和 } } final[k] = 1; /* 將目前找到的最近的頂點置爲1, 之後就不用再找 */ for(w=0; w<G.numVertexes; w++) /* 修正當前最短路徑及距離 */ { /* 若是通過v頂點的路徑比如今這條路徑的長度短的話 */ if(!final[w] && (min+G.arc[k][w]<(*D)[w])) { /* 說明找到了更短的路徑,修改D[w]和P[w] */ (*D)[w] = min + G.arc[k][w]; /* 修改當前路徑長度 */ (*P)[w]=k; } } } } |
//主函數 |
int main(void) { int i,j,v0; MGraph G; Patharc P; ShortPathTable D; /* 求某點到其他各點的最短路徑 */ v0=0;
CreateMGraph(&G);
ShortestPath_Dijkstra(G, v0, &P, &D);
printf("最短路徑倒序以下:\n"); for(i=1;i<G.numVertexes;++i) { printf("v%d - v%d : ",v0,i); j=i; while(P[j]!=0) { printf("%d ",P[j]); j=P[j]; } printf("\n"); } printf("\n源點到各頂點的最短路徑長度爲:\n"); for(i=1;i<G.numVertexes;++i) printf("v%d - v%d : %d \n",G.vexs[0],G.vexs[i],D[i]); return 0; }
|
//版本1 |
第1步:初始化矩陣S。 矩陣S中a[i][j]爲頂點i到頂點j的權值;若是i和j不相鄰,則a[i][j]=∞。 第2步:以頂點A(第1個頂點)爲中介點,若a[i][j] > a[i][0]+a[0][j],則設置a[i][j]=a[i][0]+a[0][j]。 在上面的圖例中, a[1][6]即(B,G)=∞;而將A做爲中介點時,a[i][0] 即(B,A)=12,a[0][j], 即(A,G)=14,a[i][j] > a[i][0]+a[0][j], 所以B和G之間的距離能夠更新爲26。 第3步: 依次將頂點B,C,D,E,F,G做爲中介點,並更新a[i][j]的大小。 |
/* floyd最短路徑: G -- 圖 path -- 路徑。path[i][j]=k表示,"頂點i"到"頂點j"的最短路徑會通過頂點k。 dist -- 長度數組。即,dist[i][j]=sum表示,"頂點i"到"頂點j"的最短路徑的長度是sum。 */ void floyd(Graph G, int path[][MAX], int dist[][MAX]) { int i,j,k; int tmp;
// 初始化 for (i = 0; i < G.vexnum; i++) { for (j = 0; j < G.vexnum; j++) { dist[i][j] = G.matrix[i][j]; // "頂點i"到"頂點j"的路徑長度爲"i到j的權值"。 path[i][j] = j; // "頂點i"到"頂點j"的最短路徑通過了頂點j。 } }
// 計算最短路徑 for (k = 0; k < G.vexnum; k++) { for (i = 0; i < G.vexnum; i++) { for (j = 0; j < G.vexnum; j++) { // 若是通過下標爲k頂點路徑比原兩點間路徑更短,則更新dist[i][j]和path[i][j] tmp = (dist[i][k]==INF || dist[k][j]==INF) ? INF : (dist[i][k] + dist[k][j]); if (dist[i][j] > tmp) { // "i到j最短路徑"對應的值設爲更小的一個(即通過k) dist[i][j] = tmp; // "i到j最短路徑"對應的路徑,通過k path[i][j] = path[i][k]; } } } }
// 打印floyd最短路徑的結果 printf("floyd: \n"); for (i = 0; i < G.vexnum; i++) { for (j = 0; j < G.vexnum; j++) printf("%2d ", dist[i][j]); printf("\n"); } } |
//版本2 |
//起始部分 |
#include "stdio.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h"
#define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXEDGE 20 #define MAXVEX 20 #define INFINITY 65535
typedef int Status; /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */ |
typedef struct { int vexs[MAXVEX]; int arc[MAXVEX][MAXVEX]; int numVertexes, numEdges; }MGraph;
typedef int Patharc[MAXVEX][MAXVEX]; typedef int ShortPathTable[MAXVEX][MAXVEX]; |
/* 構件圖 */ |
void CreateMGraph(MGraph *G) { int i, j;
/* printf("請輸入邊數和頂點數:"); */ G->numEdges=16; G->numVertexes=9;
for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */ { G->vexs[i]=i; }
for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */ { for ( j = 0; j < G->numVertexes; j++) { if (i==j) G->arc[i][j]=0; else G->arc[i][j] = G->arc[j][i] = INFINITY; } }
G->arc[0][1]=1; G->arc[0][2]=5; G->arc[1][2]=3; G->arc[1][3]=7; G->arc[1][4]=5;
G->arc[2][4]=1; G->arc[2][5]=7; G->arc[3][4]=2; G->arc[3][6]=3; G->arc[4][5]=3;
G->arc[4][6]=6; G->arc[4][7]=9; G->arc[5][7]=5; G->arc[6][7]=2; G->arc[6][8]=7;
G->arc[7][8]=4;
for(i = 0; i < G->numVertexes; i++) { for(j = i; j < G->numVertexes; j++) { G->arc[j][i] =G->arc[i][j]; } }
} |
/* Floyd算法,求網圖G中各頂點v到其他頂點w的最短路徑P[v][w]及帶權長度D[v][w]。 */ |
void ShortestPath_Floyd(MGraph G, Patharc *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.arc[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][k]+(*D)[k][w];/* 將當前兩點間權值設爲更小的一個 */ (*P)[v][w]=(*P)[v][k];/* 路徑設置爲通過下標爲k的頂點 */ } } } } } |
//主函數 |
int main(void) { int v,w,k; MGraph G;
Patharc P; ShortPathTable D; /* 求某點到其他各點的最短路徑 */
CreateMGraph(&G);
ShortestPath_Floyd(G,&P,&D);
printf("各頂點間最短路徑以下:\n"); for(v=0; v<G.numVertexes; ++v) { for(w=v+1; w<G.numVertexes; w++) { printf("v%d-v%d weight: %d ",v,w,D[v][w]); k=P[v][w]; /* 得到第一個路徑頂點下標 */ printf(" path: %d",v); /* 打印源點 */ while(k!=w) /* 若是路徑頂點下標不是終點 */ { printf(" -> %d",k); /* 打印路徑頂點 */ k=P[k][w]; /* 得到下一個路徑頂點下標 */ } printf(" -> %d\n",w); /* 打印終點 */ } printf("\n"); }
printf("最短路徑D\n"); for(v=0; v<G.numVertexes; ++v) { for(w=0; w<G.numVertexes; ++w) { printf("%d\t",D[v][w]); } printf("\n"); } printf("最短路徑P\n"); for(v=0; v<G.numVertexes; ++v) { for(w=0; w<G.numVertexes; ++w) { printf("%d ",P[v][w]); } printf("\n"); }
return 0; }
|
●
拓撲排序有兩種經常使用的活動網絡( Activity Network): ① AOV網(Activity On Vertices)——用頂點表示活動的網絡; ② AOE網(Activity On Edges)——用邊表示活動的網絡
按照有向圖給出的次序關係,將圖中頂點排成一個線性序列,對於有向圖中沒有限定次序關係的頂點,則能夠人爲加上任意的次序關係。由此所得頂點的線性序列稱之爲拓撲有序序列。
例如: 對於下列有向圖:
可求得拓撲有序序列: A B C D 或 A C B D
反之,對於下列有向圖:
不能求得它的拓撲有序序列, 由於圖中存在一個迴路 {B, C, D} |
對AOV網進行拓撲排序的方法和步驟:
|
//起始部分 |
#include "stdio.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h"
#define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 #define MAXEDGE 20 #define MAXVEX 14 #define INFINITY 65535
typedef int Status; /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */ |
/* 鄰接矩陣結構 */ |
typedef struct { int vexs[MAXVEX]; int arc[MAXVEX][MAXVEX]; int numVertexes, numEdges; }MGraph; |
/* 鄰接表結構 */ |
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,numEdges; /* 圖中當前頂點數和邊數 */ }graphAdjList,*GraphAdjList; |
/* 構件圖 */ |
void CreateMGraph(MGraph *G) { int i, j;
/* printf("請輸入邊數和頂點數:"); */ G->numEdges=MAXEDGE; G->numVertexes=MAXVEX;
for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */ { G->vexs[i]=i; }
for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */ { for ( j = 0; j < G->numVertexes; j++) { G->arc[i][j]=0; } }
G->arc[0][4]=1; G->arc[0][5]=1; G->arc[0][11]=1; G->arc[1][2]=1; G->arc[1][4]=1; G->arc[1][8]=1; G->arc[2][5]=1; G->arc[2][6]=1; G->arc[2][9]=1; G->arc[3][2]=1; G->arc[3][13]=1; G->arc[4][7]=1; G->arc[5][8]=1; G->arc[5][12]=1; G->arc[6][5]=1; G->arc[8][7]=1; G->arc[9][10]=1; G->arc[9][11]=1; G->arc[10][13]=1; G->arc[12][9]=1;
} |
/* 利用鄰接矩陣構建鄰接表 */ |
void CreateALGraph(MGraph G,GraphAdjList *GL) { int i,j; EdgeNode *e;
*GL = (GraphAdjList)malloc(sizeof(graphAdjList));
(*GL)->numVertexes=G.numVertexes; (*GL)->numEdges=G.numEdges; for(i= 0;i <G.numVertexes;i++) /* 讀入頂點信息,創建頂點表 */ { (*GL)->adjList[i].in=0; (*GL)->adjList[i].data=G.vexs[i]; (*GL)->adjList[i].firstedge=NULL; /* 將邊表置爲空表 */ }
for(i=0;i<G.numVertexes;i++) /* 創建邊表 */ { for(j=0;j<G.numVertexes;j++) { if (G.arc[i][j]==1) { e=(EdgeNode *)malloc(sizeof(EdgeNode)); e->adjvex=j; /* 鄰接序號爲j */ e->next=(*GL)->adjList[i].firstedge; /* 將當前頂點上的指向的結點指針賦值給e */ (*GL)->adjList[i].firstedge=e; /* 將當前頂點的指針指向e */ (*GL)->adjList[j].in++;
} } }
} |
/* 拓撲排序,若GL無迴路,則輸出拓撲排序序列並返回1,如有迴路返回0。 */ |
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(0 == GL->adjList[i].in) /* 將入度爲0的頂點入棧 */ stack[++top]=i; while(top!=0) { gettop=stack[top--]; printf("%d -> ",GL->adjList[gettop].data); count++; /* 輸出i號頂點,並計數 */ for(e = GL->adjList[gettop].firstedge; e; e = e->next) { k=e->adjvex; if( !(--GL->adjList[k].in) ) /* 將i號頂點的鄰接點的入度減1,若是減1後入度爲0,則入棧, 以便下次循環輸出 */ stack[++top]=k; } } printf("\n"); if(count < GL->numVertexes) //若是count小於頂點數, 說明存在環 return ERROR; else return OK; } |
//主函數 |
int main(void) { MGraph G; GraphAdjList GL; int result; CreateMGraph(&G); CreateALGraph(G,&GL); result=TopologicalSort(GL); printf("result:%d",result);
return 0; } |
● 關鍵路徑
路徑長度: 路徑上各個活動所持續的時間之和 關鍵路徑: 從源點到匯點具備最大長度的路徑叫關鍵路徑,在關鍵路徑上的活動叫關鍵活動。
|
//起始部分 |
#include "stdio.h" #include "stdlib.h" #include "io.h" #include "math.h" #include "time.h"
#define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0
#define MAXEDGE 30 #define MAXVEX 30 #define INFINITY 65535
typedef int Status; /* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */
int *etv,*ltv; /* 事件最先發生時間和最遲發生時間數組,全局變量 */ int *stack2; /* 用於存儲拓撲序列的棧 */ int top2; /* 用於stack2的指針 */ |
/* 鄰接矩陣結構 */ |
typedef struct { int vexs[MAXVEX]; int arc[MAXVEX][MAXVEX]; int numVertexes, numEdges; }MGraph; |
/* 鄰接表結構 */ |
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,numEdges; /* 圖中當前頂點數和邊數 */ }graphAdjList,*GraphAdjList; |
/* 構件圖 */ |
void CreateMGraph(MGraph *G) { int i, j; /* printf("請輸入邊數和頂點數:"); */ G->numEdges=13; G->numVertexes=10;
for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */ { G->vexs[i]=i; }
for (i = 0; i < G->numVertexes; i++)/* 初始化圖 */ { for ( j = 0; j < G->numVertexes; j++) { if (i==j) G->arc[i][j]=0; else G->arc[i][j]=INFINITY; } }
G->arc[0][1]=3; G->arc[0][2]=4; G->arc[1][3]=5; G->arc[1][4]=6; G->arc[2][3]=8; G->arc[2][5]=7; G->arc[3][4]=3; G->arc[4][6]=9; G->arc[4][7]=4; G->arc[5][7]=6; G->arc[6][9]=2; G->arc[7][8]=5; G->arc[8][9]=3;
} |
/* 利用鄰接矩陣構建鄰接表 */ |
void CreateALGraph(MGraph G,GraphAdjList *GL) { int i,j; EdgeNode *e;
*GL = (GraphAdjList)malloc(sizeof(graphAdjList));
(*GL)->numVertexes=G.numVertexes; (*GL)->numEdges=G.numEdges; for(i= 0;i <G.numVertexes;i++) /* 讀入頂點信息,創建頂點表 */ { (*GL)->adjList[i].in=0; (*GL)->adjList[i].data=G.vexs[i]; (*GL)->adjList[i].firstedge=NULL; /* 將邊表置爲空表 */ }
for(i=0;i<G.numVertexes;i++) /* 創建邊表 */ { for(j=0;j<G.numVertexes;j++) { if (G.arc[i][j]!=0 && G.arc[i][j]<INFINITY) { e=(EdgeNode *)malloc(sizeof(EdgeNode)); e->adjvex=j; /* 鄰接序號爲j */ e->weight=G.arc[i][j]; e->next=(*GL)->adjList[i].firstedge; /* 將當前頂點上的指向的結點指針賦值給e */ (*GL)->adjList[i].firstedge=e; /* 將當前頂點的指針指向e */ (*GL)->adjList[j].in++;
} } }
} |
/* 拓撲排序 */ |
Status TopologicalSort(GraphAdjList GL) { /* 若GL無迴路,則輸出拓撲排序序列並返回1,如有迴路返回0。 */ 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(0 == GL->adjList[i].in) /* 將入度爲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) );/* 初始化拓撲序列棧 */
printf("TopologicalSort:\t"); while(top!=0) { gettop=stack[top--]; printf("%d -> ",GL->adjList[gettop].data); count++; /* 輸出i號頂點,並計數 */
stack2[++top2]=gettop; /* 將彈出的頂點序號壓入拓撲序列的棧 */
for(e = GL->adjList[gettop].firstedge; e; e = e->next) { k=e->adjvex; if( !(--GL->adjList[k].in) ) /* 將i號頂點的鄰接點的入度減1,若是減1後爲0,則入棧 */ stack[++top]=k;
if((etv[gettop] + e->weight)>etv[k]) /* 求各頂點事件的最先發生時間etv值 */ etv[k] = etv[gettop] + e->weight; } } printf("\n"); if(count < GL->numVertexes) return ERROR; else return OK; } |
/* 求關鍵路徑,GL爲有向網,輸出G的各項關鍵活動 */ |
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]; /* 初始化 */
printf("etv:\t"); for(i=0; i<GL->numVertexes; i++) printf("%d -> ",etv[i]); printf("\n");
while(top2!=0) /* 出棧是求ltv */ { gettop=stack2[top2--]; for(e = GL->adjList[gettop].firstedge; e; e = e->next) /* 求各頂點事件的最遲發生時間ltv值 */ { k=e->adjvex; if(ltv[k] - e->weight < ltv[gettop]) ltv[gettop] = ltv[k] - e->weight; } }
printf("ltv:\t"); for(i=0; i<GL->numVertexes; i++) printf("%d -> ",ltv[i]); printf("\n");
for(j=0; j<GL->numVertexes; j++) /* 求ete,lte和關鍵活動 */ { for(e = GL->adjList[j].firstedge; e; e = e->next) { k=e->adjvex; ete = etv[j]; /* 活動最先發生時間 */ lte = ltv[k] - e->weight; /* 活動最遲發生時間 */ if(ete == lte) /* 二者相等即在關鍵路徑上 */ printf("<v%d - v%d> length: %d \n",GL->adjList[j].data,GL->adjList[k].data,e->weight); } } } |
//主函數 |
int main(void) { MGraph G; GraphAdjList GL; CreateMGraph(&G); CreateALGraph(G,&GL); CriticalPath(GL); return 0; }
|