数据结构课程设计(十一)---关键路径问题

1、任务简述:
设计实现AOE网的关键活动与关键路径问题

要求:
(1)自行建立图的数据文件,以邻接表或者邻接矩阵表示图皆可,显示输出原图(按照邻接表的样式);
(2)计算出各个事件的最早发生时间与最迟发生时间,并显示输出;
(3)输出所有的关键路径。
2、算法描述:
数据结构:
typedef struct arc
{
int index; //编号
float weight; //权重
struct arc *next; //指向下一个节点
}AR;

typedef struct MyGraph
{
int type;//0表示无向网,1表示有向网
int arcnum; //边的数量
int vexnum; //点的个数
char **vexname; //点的名字
AR *N;//邻接表
}GH;

1. 建立AOE网的存储结构。
2. 拓扑排序,并求得ve[]。从源点V0出发,令ve[0]=0,按拓扑有序求其余各顶点的最早发生时间ve[i]。如果得到的拓扑有序序列中顶点个数小于网中顶点数n,则说明网中存在环,不能求关键路径,算法终止;否则执行步骤3。
3. 拓扑逆序,求得vl[]。从汇点Vn出发,令vl[n-1] = ve[n-1],按逆拓扑有序求其余各顶点的最迟发生时间vl[i]。
4. 求得关键路径。根据各顶点的ve和vl值,求每条弧s的最早开始时间e(s)和最迟开始时间l(s)。若某条弧满足条件e(s) = l(s),则为关键活动。

3、源代码

#include 
#include 
#include 

int flag=0;

typedef struct arc
{
	int index; //编号 
	float weight; //权重 
	struct arc *next; //指向下一个节点 
}AR;

typedef struct MyGraph
{
	int type;//0表示无向网,1表示有向网
	int arcnum; //边的数量 
	int vexnum;  //点的个数 
	char **vexname; //点的名字 
	AR *N;//邻接表 
}GH;

int findvex(GH *G,char *s);//确定顶点s对应的编号号
void creatgraph(GH *G);//以邻接表的形式创建图
void showgraph(GH *G);//以邻接表的形式显示图
int tuopu(GH *G,int *t);//获得拓扑排序 
void keypath(GH *G);   //算出每个点的最早发生时间和最晚发生时间
void allkeypath(GH *G,int head,int end,int *path,int n,int length,int *visit,int *isequl);//显示所有关键路径(不止一条)

main()
{
	GH G;
	system("color 1E");
	printf("--------------081810221朱林昊--------------\n");
	printf("\n--------------关键路径--------------\n\n");  //说明该代码的实现功能 
	printf("\n原图为:\n");
	creatgraph(&G);//创建图
	showgraph(&G);//显示图
	keypath(&G);
}

int findvex(GH *G,char *s)
{
	int i;
	for(i=0;i<G->vexnum;i++)
	{
		if(strcmp(G->vexname[i],s)==0)//判断字符串是否一样 
			return i;
	}
	printf("Error!\n");
	exit(0);
}

void creatgraph(GH *G)
{
	FILE *fp;
	int i,j,n;
	float k;//记录权重 
	char s1[20],s2[20];
	AR *p;
	fp=fopen("graph2.txt","rb");
	if(!fp)
	{
		printf("Can not open file!!!\n");
		exit(0);
	}
	fscanf(fp,"%d",&n);
	G->vexnum=n; //点的数量 
	G->type=1;  //有向图 
	G->N=(AR *)malloc(n*sizeof(AR));
	G->vexname=(char **)malloc(n*sizeof(char *));
	G->arcnum=0; //边的数量初始化为0 
	for(i=0;i<n;i++)//初始化 
	{
		fscanf(fp,"%s",s1);
		G->vexname[i]=(char *)malloc(strlen(s1)*sizeof(char));
		strcpy(G->vexname[i],s1);
		G->N[i].next=NULL;		
	}
	while(fscanf(fp,"%s%s%f",s1,s2,&k)!=EOF)//读入边的两个顶点和权重 
	{
		p=(AR *)malloc(sizeof(AR));
		i=findvex(G,s1);
		j=findvex(G,s2);
		(G->arcnum)++;
		p->index=j;
		p->weight=k;
		p->next=G->N[i].next;
		G->N[i].next=p;
		if(G->type==0)
		{
			p=(AR *)malloc(sizeof(AR));
			p->index=i;
			p->weight=k;
			p->next=G->N[j].next;
			G->N[j].next=p;
		}
	}
	fclose(fp);//关闭文件	
}

void showgraph(GH *G)//用邻接表显示图 
{
	int i;
	AR *p;
	for(i=0;i<G->vexnum;i++)
	{
		printf("\n%s",G->vexname[i]);
		p=G->N[i].next;
		while(p)
		{			
			printf("---%s(%.2f)",G->vexname[p->index],p->weight);
			p=p->next;
		}
		printf("\n");	
	}
	printf("\n");
}

int tuopu(GH *G,int *t) 
{
	int *zhan,*rudu;//zhan是栈,将入度为0的点入栈,rudu是记录每个点入度的数组,用来寻找入度为0的点 
	int top=-1,count=0,i;//top为栈顶,count表示 
	AR *p;
	zhan=(int *)malloc(G->vexnum*sizeof(int));
	rudu=(int *)malloc(G->vexnum*sizeof(int));
	memset(rudu,0,G->vexnum*sizeof(int));
	for(i=0;i<G->vexnum;i++)//求每个点的入度
	{
		p=G->N[i].next;
		while(p)
		{
			rudu[p->index]++;
			p=p->next;
		}
	}
	for(i=0;i<G->vexnum;i++)//将入度为0的点入栈 
	{
		if(rudu[i]==0)
		{
			top++;
			zhan[top]=i;
		}
	}
	while(top>=0)
	{
		i=zhan[top];
		top--;
		t[count]=i;
		count++;
		p=G->N[i].next;
		while(p)//将所有该点连接的点的入度都减一 
		{
			rudu[p->index]--;
			if(rudu[p->index]==0)//发现减完后,有点入度为0,入栈 
			{
				top++;
				zhan[top]=p->index;
			}
			p=p->next;
		}
	}
	free(zhan);
	free(rudu);
	if(count==G->vexnum)//判断是否所有点都在拓扑排序里面,是的话返回1,否则因为没有所有点进入排序,说明排序失败:返回0 
		return 1;
	else
		return 0;
}

void keypath(GH *G)
{
	int *t,*ve,*vl;   //t:记录拓扑序列,ve:最早开始时间,vl:最迟开始时间 
	int *visit,*path,*isequl;//visit记录每个点是否被访问,path记录路径上的点,isok记录每个点最早开始时间和最迟是否相等,1代表相等,0代表不等 
	int i;
	int max;//用来寻找最早开始时间的最大值 
	AR *p;
	t=(int *)malloc(G->vexnum*sizeof(int));
	ve=(int *)malloc(G->vexnum*sizeof(int));
	vl=(int *)malloc(G->vexnum*sizeof(int));
	visit=(int *)malloc(G->vexnum*sizeof(int));
	path=(int *)malloc(G->vexnum*sizeof(int));
	isequl=(int *)malloc(G->vexnum*sizeof(int));
	
	memset(ve,0,G->vexnum*sizeof(int));
	memset(visit,0,G->vexnum*sizeof(int));
	memset(isequl,0,G->vexnum*sizeof(int));
	if(!tuopu(G,t))//拓扑排序并输出拓扑序列 
	{
		printf("图中有回路,不存在关键路径!\n");
		exit(0);
	}
	//printf("\n该图的拓扑序列是: "); //输出拓扑路径,没有输出 
	//for(i=0;ivexnum;i++)
		//printf("%s ",G->vexname[t[i]]);
	//printf("\n\n");
	for(i=0;i<G->vexnum;i++)//求每个点的最早开始时间并输出,从前往后 
	{
		p=G->N[t[i]].next; 
		while(p)
		{
			if(ve[p->index]<(ve[t[i]]+p->weight))//判断更新最小的时间 
				ve[p->index]=ve[t[i]]+p->weight;
			p=p->next;
		}
	}
	printf("\n最早发生时间:\n");
	max=ve[0];
	for(i=0;i<G->vexnum;i++)
	{
		if(ve[i]>max)
			max=ve[i];
		printf("%s: %d\n",G->vexname[i],ve[i]);
	}
	printf("\n");
	for(i=0;i<G->vexnum;i++)//给每个点的最迟开始时间赋初值,为最早开始时间 
		vl[i]=max;
	for(i=G->vexnum-1;i>=0;i--)//求每个点的最迟开始时间并输出,思路同上,改变>即可 
	{
		p=G->N[t[i]].next; 
		while(p)
		{
			if(vl[t[i]]>(vl[p->index]-p->weight))
				vl[t[i]]=vl[p->index]-p->weight;
			p=p->next;
		}
	}
	printf("最迟发生时间:\n");
	for(i=0;i<G->vexnum;i++)
	{
		printf("%s: %d\n",G->vexname[i],vl[i]);
		if(vl[i]==ve[i])//将最早开始时间和最迟开始时间相等的点的isequal记为1,寻找关键路径 
			isequl[i]=1;
	}
	printf("\n");
	path[0]=t[0];
	allkeypath(G,t[0],t[G->vexnum-1],path,1,0,visit,isequl);//递归调用allkeypath函数显示所有关键路径 
}

void allkeypath(GH *G,int head,int end,int *path,int n,int length,int *visit,int *isequl)//path用来存储路径,这个仿照了之前迷宫的路径输出方式 
{
	if(head==end)//到达终点 
	{
		int i;
		flag++;
		printf("第%d条关键路径:",flag);
		for(i=0;i<n-1;i++)
			printf("%s-->",G->vexname[path[i]]);
		printf("%s   路径长度为:%d\n\n",G->vexname[path[i]],length);
	}
	else
	{
		AR *p;
		p=G->N[head].next;
		while(p)//遍历每个点的所有后继 
		{
			if(!visit[p->index]&&isequl[p->index])
			{
				path[n]=p->index;
				visit[p->index]=1;
				allkeypath(G,p->index,end,path,n+1,length+p->weight,visit,isequl);//递归调用自己,显示所有关键路径 
				visit[p->index]=0;
			}
			p=p->next;
		}
	}
}//共267行 

4、运行结果
数据结构课程设计(十一)---关键路径问题_第1张图片
5、总结
性能分析:
时间复杂度:如果AOV网络有n个顶点,e条边,在拓扑排序的过程中,搜索入度为零的顶点所需的时间是O(n)。在正常情况下,每个顶点进一次栈,出一次栈,所需时间O(n)。每个顶点入度减1的运算共执行了e次。所以总的时间复杂为O(n+e)。
空间复杂度:O(n),存储拓扑路径
心得体会:
运行结果正确,成功创建图,并且找出其拓扑路径以及关键路径。并且这道题我用上次作业题来进行测试,发现运行结果正确,但是要注意,不能简单的把最早开始时间的最后一个直接赋值给最晚开始时间,因为v6不一定是终点,想这题,v3才是终点,所以在输出最早开始时间时,可以来一个遍历,判断,找出最大值。

你可能感兴趣的:(C语言,课程设计,数据结构,算法)