1. 程式人生 > >資料結構--C語言--圖的深度優先遍歷,廣度優先遍歷,拓撲排序,用prime演算法實現最小生成樹,用迪傑斯特拉演算法實現關鍵路徑和關鍵活動的求解,最短路徑

資料結構--C語言--圖的深度優先遍歷,廣度優先遍歷,拓撲排序,用prime演算法實現最小生成樹,用迪傑斯特拉演算法實現關鍵路徑和關鍵活動的求解,最短路徑

實驗七  圖的深度優先遍歷選做,驗證性實驗,4學時)

  1. 實驗目的

熟悉圖的陣列表示法和鄰接表儲存結構,掌握構造有向圖、無向圖的演算法 ,在掌握以上知識的基礎上,熟悉圖的深度優先遍歷演算法,並實現。

  1. 實驗內容

(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;
}