数据结构--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;
}

 

    原文作者:数据结构之图
    原文地址: https://blog.csdn.net/J_SUNV/article/details/85047656
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞