資料結構--C語言--圖的深度優先遍歷,廣度優先遍歷,拓撲排序,用prime演算法實現最小生成樹,用迪傑斯特拉演算法實現關鍵路徑和關鍵活動的求解,最短路徑
阿新 • • 發佈:2018-12-17
實驗七 圖的深度優先遍歷(選做,驗證性實驗,4學時)
- 實驗目的
熟悉圖的陣列表示法和鄰接表儲存結構,掌握構造有向圖、無向圖的演算法 ,在掌握以上知識的基礎上,熟悉圖的深度優先遍歷演算法,並實現。
- 實驗內容
(1)圖的陣列表示法定義及基本操作的實現。
(2)圖的鄰接表表示法定義及基本操作的實現。
(3)寫函式實現圖的深度優先遍歷(分別在兩種結構上)
(4)在鄰接表上實現拓撲排序、關鍵路徑的求法,在鄰接矩陣上實現最短路經、最小生成樹的求法。
//鄰接矩陣表示法儲存無向圖 深度優先搜尋 最短路徑 最小生成樹 #include<stdio.h> #define MAX_VERTEX_NUM 20 #define INT_MAX unsigned(-1)>>1 typedef int VRType; typedef int Status; typedef char VertexType; typedef char* InfoType; typedef int PathMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; typedef int ShortPathTable[MAX_VERTEX_NUM]; int visited[MAX_VERTEX_NUM];//全域性變數 訪問標誌陣列 typedef enum{DG,DN,UDG,UDN}GraphKind; typedef struct{ VertexType adjvex; int lowcost; }Closedge[MAX_VERTEX_NUM];//存放所有已經插入的節點到當前頂點的權值最小的那條邊 終點就是i位置上的頂點 adj存放邊的起點 lowcost存放最小的權值 typedef struct{ VRType adj;//頂點關係 InfoType info;//該弧相關資訊的指標 --權值 }ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; typedef struct{ VertexType vexs[MAX_VERTEX_NUM];//頂點向量 AdjMatrix arcs;//鄰接矩陣 int vexnum,arcnum; GraphKind kind;//圖的種類標誌 }MGraph; int LocateVex(MGraph G,VertexType a){ for(int i=0;i<G.vexnum;i++) if(G.vexs[i]==a) return i; } Status CreatUDN(MGraph &G){ int IncInfo; G.kind = GraphKind(UDN); printf("請輸入頂點數 邊數 邊上是否有相關資訊(是1否0)\n"); scanf("%d%d%d",&G.vexnum,&G.arcnum,&IncInfo);//頂點個數 邊數 //先構造頂點向量 給頂點陣列賦值 fflush(stdin); printf("請輸入頂點集:\n"); for(int i=0;i<G.vexnum;i++) scanf(" %c",&G.vexs[i]); //初始化鄰接矩陣 for(int i=0;i<G.vexnum;i++) for(int j=0;j<G.vexnum;j++) G.arcs[i][j].adj=INT_MAX;//初始化全是斷路 printf("請輸入弧頭弧尾和權值\n"); for(int i=0;i<G.arcnum;i++){ char head,tail;//弧頭弧尾的兩個字元 int weight,headlo,taillo;//邊的權值 弧頭弧尾在頂點向量中的位置 fflush(stdin); scanf(" %c %c %d",&tail,&head,&weight); headlo = LocateVex(G,head); taillo = LocateVex(G,tail); G.arcs[taillo][headlo].adj = weight; if(IncInfo) scanf("%s",G.arcs[headlo][taillo].info); // G.arcs[taillo][headlo] = G.arcs[headlo][taillo];//無向圖的邊的關係在矩陣裡是對稱的 } } Status Visit(VertexType c){ printf("%c",c); } int FirstAdjVex(MGraph G,int v){ for(int j=0;j<G.vexnum;j++) if(G.arcs[v][j].adj) return j; } int NextAdjVex(MGraph G,int v,int w){ for(int j=w+1;j<G.vexnum;j++) if(G.arcs[v][j].adj) return j; return -1; } Status DFS(MGraph G,int i){ visited[i] = 1; Visit(G.vexs[i]); for(int w=FirstAdjVex(G,i);w>=0;w=NextAdjVex(G,i,w)) if(!visited[w]) DFS(G,w); return 0; } Status DFSTraverse(MGraph G,Status (*Visit)(VertexType c)){//深度優先遍歷演算法 for(int i=0;i<G.vexnum;i++) visited[i] = 0; for(int i=0;i<G.vexnum;i++) if(!visited[i]) DFS(G,i); return 0; } void MiniSpanTree_PRIME(MGraph G,VertexType u){//從u頂點出發開始構造最小生成樹 int k = LocateVex(G,u),minlocate;//新插入節點在頂點向量裡的下標 Closedge closedge; for(int i=0;i<G.vexnum;i++){//輔助陣列初始化 if(i!=k){ closedge[i].adjvex = u;//邊的起點 closedge[i].lowcost = G.arcs[k][i].adj;//邊的權值 } } closedge[k].lowcost = 0; minlocate = 0; for(int i=1;i<G.vexnum;i++){//選擇其餘的i-1個頂點 for(int m=0;m<G.vexnum;m++){ if(!closedge[m].lowcost) continue; if(!closedge[minlocate].lowcost) minlocate = m; if(closedge[minlocate].lowcost>closedge[m].lowcost) minlocate = m; } k = minlocate;//選出權值最小的邊的終點的下標 printf("\n%c %c",closedge[k].adjvex,G.vexs[k]); closedge[k].lowcost = 0;//第k頂點已經插入在樹裡了 所以不會存在以第k頂點為終點的待插入的邊了 for(int j=0;j<G.vexnum;j++){ if(G.arcs[k][j].adj<closedge[j].lowcost){ closedge[j].lowcost = G.arcs[k][j].adj; closedge[j].adjvex = G.vexs[k]; } } } } void ShortestPath_DIJ(MGraph G,VertexType e,PathMatrix &P,ShortPathTable &D){ int final[MAX_VERTEX_NUM]; int v0 = LocateVex(G,e); int newin,min; //初始化D[] P[][] final[] for(int i=0;i<G.vexnum;i++){ D[i] = G.arcs[v0][i].adj; final[i] = 0;//均設定為沒有找到最短路徑 for(int j=0;j<G.vexnum;j++) P[i][j] = 0;//設定空路徑 if(D[i]<INT_MAX){ P[i][v0] = 1;//權值不是無窮大 說明存在通路 P[i][i] = 1; } } D[v0] = 0;//從源點到源點的最小路徑為0 final[v0] = 1;//初始化v0 v0屬於S集 //開始主迴圈 每次求得v0到某個頂點v的最短路徑 並加v到S集 for(int i=1;i<G.vexnum;i++){ min = INT_MAX; for(int j=0;j<G.vexnum;j++){ if(!final[j]){//min存放的是當前與v0最近的的節點的路徑 newin是最近的頂點的下標 也就是即將插入S的頂點 if(D[j]<min){ min = D[j]; newin = j; } } } final[newin] = 1;//將第j個頂點插入S中 說明該頂點已經找到了最短路徑 for(int j=0;j<G.vexnum;j++){ if(!final[j]&&(D[newin]+G.arcs[newin][j].adj>0)&&(D[newin]+G.arcs[newin][j].adj<D[j])){//修改D[w]和P[w] D[j] = D[newin]+G.arcs[newin][j].adj; for(int m=0;m<G.vexnum;m++) P[j][m] = P[newin][m]; P[j][newin] = 1; P[j][j]=1; } } } printf("endvex\tpastvex\tpathlength\n"); for(int i=0;i<G.vexnum;i++){ printf(" %c \t",G.vexs[i]); if(D[i]<INT_MAX){ for(int j=0;j<G.vexnum;j++) if(P[i][j]) printf("%c",G.vexs[j]); printf(" \t%d\n",D[i]); }else{ printf("無\n"); } } } main(){ MGraph G; CreatUDN(G); printf("\n\n深度優先遍歷:"); DFSTraverse(G,Visit); printf("\n\n最小生成樹的構成:"); MiniSpanTree_PRIME(G,'a'); PathMatrix P; ShortPathTable D; printf("\n\n從源點a到其餘各點的最短路徑:\n"); ShortestPath_DIJ(G,'a',P,D); return 0; }
//鄰接表表示法儲存有向圖 廣度優先搜尋拓 撲排序 關鍵路徑 #include<stdio.h> #include<stdlib.h> #define MAXSIZE 20 #define MAX_VERTEX_NUM 20 typedef int Status; typedef int QElemType;//佇列裡放的是元素在一位數組裡的下標 typedef char VertexType; typedef int SElemType; typedef enum{DG,DN,UDG,UDN}GraphKind; typedef struct{ QElemType *base;//佇列的基地址 int front;//隊頭下標 相當於隊頭指標 指向佇列頭元素 int rear;//隊尾下標 指向佇列尾元素的下一個位置 }SqQue; typedef struct ArcNode{//表結點 int adjvex;//該弧指向的頂點的位置 int weight;//權重 int info; struct ArcNode* next; }ArcNode; typedef struct{//頭結點 VertexType data;//頂點資訊 ArcNode *firstarc;//指向第一個鄰接點 }VNode,AdjList[MAX_VERTEX_NUM]; typedef struct{ AdjList vertices; int vexnum,arcnum; int kind; }ALGraph; typedef struct{ SElemType *base; SElemType *top; int stacksize; }SqStack; int visited[MAX_VERTEX_NUM]; int ve[MAX_VERTEX_NUM]; int vl[MAX_VERTEX_NUM]; int indegree[MAX_VERTEX_NUM]; SqStack S,T;//S是零入度頂點棧 T是拓撲序列頂點棧 int LocateVex(ALGraph G,VertexType v){ for(int i=0;i<G.vexnum;i++) if(G.vertices[i].data==v) return i; } int FirstAdjVex(ALGraph G,int v){ if(!G.vertices[v].firstarc) return -1;//不存在鄰接點 return G.vertices[v].firstarc->adjvex; } int NextAdjVex(ALGraph G,int v,int w){ ArcNode *p; for(p=G.vertices[v].firstarc;p;p=p->next) if(p->adjvex==w&&p->next) return p->next->adjvex; return -1; } Status Visit(VertexType c){ printf("%c",c); } //初始化一個空棧 Status InitStack(SqStack &S){ S.base = (SElemType *)malloc(MAX_VERTEX_NUM*sizeof(SElemType)); if(!S.base) exit(-1); S.top = S.base; S.stacksize = MAX_VERTEX_NUM; return 1; } //判空 Status StackEmpty(SqStack S){ return S.base==S.top; } //入棧 Status Push(SqStack &S,SElemType e){ *S.top++ = e; return 1; } //出棧 Status Pop(SqStack &S,SElemType &i){ if(S.base==S.top){ printf("空棧!\n"); return 0; } i = *--S.top; return 1; } Status CreatDN(ALGraph &G){ int IncInfo; printf("請輸入頂點數 邊數 邊上是否有相關資訊(是1否0)\n"); scanf("%d%d%d",&G.vexnum,&G.arcnum,&IncInfo);//頂點個數 邊數 //先構造頂點向量 fflush(stdin); printf("請輸入頂點集:\n"); for(int i=0;i<G.vexnum;i++){ scanf(" %c",&G.vertices[i].data); G.vertices[i].firstarc = NULL;//初始化指標為空 } printf("請輸入弧頭 弧尾 "); if(IncInfo) printf("邊上的關係:\n"); for(int i=0;i<G.arcnum;i++){ char head,tail;//弧頭弧尾的兩個字元 int weight,headlo,taillo;//邊的權值 弧頭弧尾在頂點向量中的位置 ArcNode *pnew; fflush(stdin); pnew = (ArcNode*)malloc(sizeof(ArcNode)); if(!pnew) exit(0); scanf(" %c %c",&head,&tail); if(IncInfo) scanf("%d",&(pnew->info)); headlo = LocateVex(G,head); pnew->adjvex = LocateVex(G,tail); pnew->next = G.vertices[headlo].firstarc; G.vertices[headlo].firstarc = pnew;//頭插法插入表結點 } } //構造一個空的迴圈佇列 Status InitQueue(SqQue &q){ q.base = (QElemType *)malloc(MAXSIZE*sizeof(QElemType)); if(!q.base) exit(0); q.front = q.rear = 0;//空佇列 兩指標均指向下表為0的位置 return 1; } //出佇列 Status DeQueue(SqQue &q,int &e){ if(q.rear==q.front) return 0; e = q.base[q.front]; q.front = (q.front+1)%MAXSIZE; return 1; } //入佇列 Status EnQueue(SqQue &q,int e){ if((q.rear+1)%MAXSIZE==q.front){ printf("佇列已滿!\n"); return 0; } q.base[q.rear] = e; q.rear = (q.rear+1)%MAXSIZE; return 1; } Status BFSTraverse(ALGraph G,Status (*Visit)(VertexType c)){//深度優先遍歷演算法 for(int i=0;i<G.vexnum;i++) visited[i] = 0; SqQue q; int u;//儲存出佇列元素 InitQueue(q); for(int i=0;i<G.vexnum;i++){ if(!visited[i]){ visited[i] = 1; Visit(G.vertices[i].data); EnQueue(q,i);//將下標入佇列 而不是元素值 while(q.rear!=q.front){ DeQueue(q,u); for(int j=FirstAdjVex(G,u);j>=0;j=NextAdjVex(G,u,j)){ if(!visited[j]){ visited[j] = 1; Visit(G.vertices[j].data); EnQueue(q,j); } } } } } } Status FindInDegree(ALGraph G){ ArcNode *p; for(int i=0;i<G.vexnum;i++) indegree[i] = 0; for(int i=0;i<G.vexnum;i++) for(p=G.vertices[i].firstarc;p;p=p->next) indegree[p->adjvex]++; } Status TopologicalSort(ALGraph G){ SqStack S; int i,j,count; ArcNode* p; FindInDegree(G); InitStack(S); for(i=0;i<G.vexnum;i++) if(!indegree[i]) Push(S,i);//將入度為0的頂點的下標入棧 count = 0;//對輸出頂點進行計數 while(!StackEmpty(S)){ Pop(S,i);//將入度為0的頂點位置出棧 printf("%c",G.vertices[i].data);//輸入出棧頂點的內容 count++;//計數器+1 for(p=G.vertices[i].firstarc;p;p=p->next){ j = p->adjvex; if(!(--indegree[j])) Push(S,j); } } if(count<G.vexnum) printf("該有向圖中有環存在\n"); } Status TopologicalOrder(ALGraph G){ int i,j,count=0; ArcNode *p; FindInDegree(G); InitStack(S);InitStack(T); for(i=0;i<G.vexnum;i++) if(!indegree[i]) Push(S,i);//將入度為0的頂點的下標入棧 for(int i=0;i<G.vexnum;i++) ve[i] = 0;//ve[i]指的是事件i的最早開始時間 while(!StackEmpty(S)){ Pop(S,i); Push(T,i); count++;//和拓撲排序裡的大致一樣 for(p=G.vertices[i].firstarc;p;p=p->next){ j = p->adjvex; if((ve[i]+p->info)>ve[j]) ve[j] = ve[i] + p->info;//計算入度為0的頂點的鄰接點的最早開始時間 indegree[j]--; if(!indegree[j]){ Push(S,j); } } } if(count<G.vexnum) return 0; return 1; } Status CriticalPath(ALGraph G){ int j,k,dut,ee,el;//ee是活動的最早開始時間 el是活動的最晚開始時間 ArcNode *p; if(!TopologicalOrder(G)) return 0;//如果該帶權有向圖存在環的話就有活動以自身為開始條件 出現錯誤 for(int i=0;i<G.vexnum;i++) vl[i] = ve[G.vexnum-1]; while(!StackEmpty(T)){//求事件的最晚發生時間 for(Pop(T,j),p=G.vertices[j].firstarc;p;p=p->next){ k = p->adjvex; dut = p->info;//關鍵活動的時間 if(vl[k]-dut<vl[j]) vl[j] = vl[k]-dut; } } printf("startevent\tendevent\tlasttime\tstarttime\n"); for(int j=0;j<G.vexnum;j++){ for(p=G.vertices[j].firstarc;p;p=p->next){ k = p->adjvex; dut = p->info; ee = ve[j]; el = vl[k]-dut; if(ee==el) printf(" %c\t %c\t %d\t %d\n",G.vertices[j].data,G.vertices[k].data,dut,ee); } } } main(){ ALGraph G; CreatDN(G); printf("\n\n廣度優先搜尋:"); BFSTraverse(G,Visit); printf("\n\n拓撲排序結果:"); TopologicalSort(G); printf("\n\n關鍵活動:\n"); CriticalPath(G); return 0; }