C语言实现图的关键路径算法

上一篇文章的拓扑排序主要是为了解决一个工程能否顺利进行的问题,但有时我们还需要解决工程完成需要的最短时间的问题。

在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,用边上的权值表示活动的持续时间,这种有向图的边表示活动的网成为AOE网(Acitivity On Edge Network)。

AOE网具有这样的特性,在某顶点所代表的事件发生后,从该顶点出发的各活动才能开始。只有在进入某顶点的各活动都已经结束,该顶点所代表的事件才能发生。

把路径上各个活动所持续的时间之和称为路径长度,从源点到汇点具有最大长度的路径叫关键路径,在关键路径上的活动叫关键活动。

只有缩短关键路径上关键活动的时间才可以减少整个工期长度。

只需要找到所有活动的最早开始时间和最晚开始时间,并且比较它们,如果相等就意味着此活动是关键活动,活动间的路径为关键路径。如果不等,则就不是。

以下程序在DEV C++下编译运行通过。

#include 
#include 
#define MAXVEX 20
	int *etv,*ltv;//事件最早发生时间和最迟发生时间数组
	int *Stack2;//用于存储拓扑序列的栈
	int top2;//用于Stack2的指针 

 

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;

void CreateALGraph(GraphAdjList *G)
{
	int i,j,k,w;
	EdgeNode *e;
	printf("输入顶点数和边数:\n");
	scanf("%d,%d",&G->numVertexes,&G->numEdges);
	getchar();
	for(i=0;inumVertexes;i++)
	{
		printf("输入第%d个顶点的值和入度:\n",i+1);
		scanf("%c,%d",&G->adjList[i].data,&G->adjList[i].in);
		G->adjList[i].firstedge=NULL;
		getchar();
	}	
	for(k=0;knumEdges;k++)
	{
		printf("输入第%d条有向边(vi,vj)上的顶点序号和权值:\n",k+1);
		scanf("%d,%d,%d",&i,&j,&w);
		e=(EdgeNode*)malloc(sizeof(EdgeNode));
		e->adjvex=j;
		e->weight=w;
		e->next=G->adjList[i].firstedge;
		G->adjList[i].firstedge=e;
	}
}

int TopologicalSort1(GraphAdjList *G)
{
	EdgeNode *e;
	int i,k,gettop;
	int top=0;//用于栈指针下标
	int count=0;//用于统计输出顶点的个数
	int *Stack;//建栈存储入度为0的指针
	Stack=(int*)malloc(G->numVertexes*sizeof(int));
	for(i=0;inumVertexes;i++)
	{
		if(G->adjList[i].in==0)
			Stack[++top]=i;//将入度为0的顶点的序号入栈 
	 } 
	 top2=0;
	 etv=(int *)malloc(G->numVertexes*sizeof(int));//时间最早发生时间数组
	 for(i=0;inumVertexes;i++)
	 {
	 	etv[i]=0;//初始化为0 
	  } 
	  Stack2=(int *)malloc(G->numVertexes*sizeof(int));
	 while(top!=0)
	 {
	 	gettop=Stack[top--];
	 	//printf("%c->",G->adjList[gettop].data);
	 	count++;
	 	Stack2[++top2]=gettop;//将弹出的顶点序号压入拓扑序列的栈 
	 	for(e=G->adjList[gettop].firstedge;e;e=e->next)
	 	{
	 		//对此顶点的弧表进行遍历
			 k=e->adjvex;//邻接点的序号
			 if(!(--G->adjList[k].in))
			 	Stack[++top]=k; 
			if((etv[gettop]+e->weight>etv[k]))
			{
				etv[k]=etv[gettop]+e->weight;
			}
		 }
	 }
	 if(countnumVertexes)
	 	return -1;
	else 
		return 0;
}

void CriticalPath(GraphAdjList *G)
{
	EdgeNode *e;
	int i,gettop,k,j;
	int ete,lte;//声明活动最早发生时间和最迟发生时间变量
	
	ltv=(int *)malloc(G->numVertexes*sizeof(int));
	for(i=0;inumVertexes;i++)
		ltv[i]=etv[G->numVertexes-1];//初始化ltv 
//	printf("top2:%d\n",top2);
//	for(i=0;i<10;i++)
//		printf("Stack2[%d]:%d",i,Stack2[i]);
	while(top2!=0) //计算ltv
	{
		gettop=Stack2[top2--];//将拓扑序列出栈
		for(e=G->adjList[gettop].firstedge;e;e=e->next)
		{
			k=e->adjvex;
			if(ltv[k]-e->weightweight;
		 } 
	 } 
	 for(j=0;jnumVertexes;j++)
	 {
	 	for(e=G->adjList[j].firstedge;e;e=e->next)
	 	{
	 		k=e->adjvex;
	 		ete=etv[j];
	 		lte=ltv[k]-e->weight;
	 		if(ete==lte)
	 			printf("<%c,%c> length: %d,",G->adjList[j].data,G->adjList[k].data,e->weight);
		 }
	 }
}
void main()
{
	GraphAdjList G;
	CreateALGraph(&G);
	TopologicalSort1(&G);//求拓扑序列,计算数组etv和Stack2的值
	CriticalPath(&G);
}


运行结果如下图所示。

C语言实现图的关键路径算法_第1张图片

输入第9条有向边(vi,vj)上的顶点序号和权值:

4,7,4

C语言实现图的关键路径算法_第2张图片

整个算法的时间复杂度为O(n+e)。

你可能感兴趣的:(数据结构和算法)