数据结构——关键路径(C语言)

1.定义

通常把计划、施工过程、生产流程、程序流程等都当成一个工程。工程通常分为若干个 称为“活动”的子工程。完成了这些“活动”,这个工程就可以完成了。通常用 AOE-网来 表示工程。AOE-网是一个带权的有向无环图,其中,顶点表示事件,弧表示活动,权表示活动持续的时间。
对 AOE-网有待研究的问题是:(1)完成整项工程至少需要多少时间?(2)哪些活动 是影响工程进度的关键? 路径长度最长的路径叫做关键路径
程序需输出(1)关键活动 (2)关键路径 (3)花费最少时间

2.算法设计

2.1步骤

1.求关键路径时需要求出:
(1)事件 i 的最早发生时间 Ve(i)
(2)事件 i 的最晚发生时间 Vl(i)
(3)活动 ai 的最早开始时间 e(i)
(4)活动 ai 的最晚开始时间 l(i)
(5)关键活动——即 e(i)=l(i)的活动

2.算法步骤
(1)以邻接表作为存储结构
(2)从源点 V1 出发,令 Ve[1]=0,按拓扑序列求各顶点的Ve[i]
(3)从汇点 Vn 出发,令 Vl[n]=Ve[n],按逆拓扑序列求其余各顶点的Vl[i]
(4)根据各顶点的Ve和Vl值,计算每条弧的e[i]和l[i]找出 e[i]=l[i]的关键活动。

3.创建邻接表
用邻接表的算法来建立图,在邻接表的顶点增加一项数据为入度,用来保存每个结点的入度。通过遍历邻接表可以将每个元素的入度求出。
首先将所有点入度初始化为 0,在每个顶点后搜索链接的结点,每遍历到一个结点,该结点所指向的元素入度加一。最终求出入度。邻接表建立成功。

4.拓扑排序
在拓扑排序过程中,求顶点的 Ve[i],将得到的拓扑序列进栈。
(1)邻接表中所有入度为 0 的顶点进栈。
(2)栈非空时,输出栈顶元素 Vj 并退栈;在邻接表中查找 Vj 的直接后继 Vk,把 Vk 的入度减 1;若 Vk 的入度为 0 则进栈。
(3)重复以上操作直至栈空为止。若栈空时输出的顶点个数不是 n,则有向图有环; 否则,拓扑排序完毕。

5.求事件最晚发生时间 Vl(i)
根据逆拓扑排序从汇点向前求各顶点的 Vl[i],从 Vl(n-1)=Ve(n-1)起向后递推:Vl[i]=Min{Vl[j]-dut()} ∈S,i=n-2,…,0
S是所有以第 i 个顶点为头 的弧的集合。

6.求关键活动
根据已求得的 ve,vl 数组求出各活动的最早开始时间 ee 和最晚开始时间 el,当ee=el时此时活动为关键活动。

7.求关键路径
求关键路径时,用深度优先搜索搜索出现的路径。首先需要设置一个一维数组用来标记关键事件,当元素为0时,该事件不是关键事件;当元素为1时,该事件是关键事件;当元素为2时,该事件是关键事件且已被访问过。
再设置一个二维数组用来标记关键活动,当两事件都为关键事件且两事件间的活动为关键活动时,才会继续往下搜索。
每搜索到一个关键事件时,将其压入栈,将该事件标记为 2,表示已经访问过。直到最后一个事件进栈,将栈中所有事件输出,并令栈顶元素出栈,将其事件标记为 1,表示没有访问,再次搜索其它路径。
直至所有路径输出。

栈的存储结构

typedef struct{ //栈
	SElemType *base;
	SElemType *top;//栈顶指针 
	int stacksize; //已分配的存储空间 
	int size; //栈的大小 
}SqStack;

算法代码

#include "stdio.h" 
#include "stdlib.h" 
#define InfoType char //存储弧或者边额外信息的指针变量类型 
#define VertexType char //顶点信息 
#define MAX_VERTEX_NUM 20 
#define STACK_INIT_SIZE 100 
#define STACKINCREMENT 10 
typedef int SElemType; 
int ve[MAX_VERTEX_NUM]; //事件最早发生时间 
int vl[MAX_VERTEX_NUM]; //事件最晚发生时间 int visited[MAX_VERTEX_NUM]; //DFS 标记数组 
int xs[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; //标记路径 DFS 
int xx; 

//边表结点 
typedef struct ArcNode{ 
	int adjvex; //该弧指向的顶点位置 
	int weight; //权值 
	struct ArcNode *nextarc; //指向下一条弧的指针 
	InfoType *info; //该弧相关信息的指针 
}ArcNode,*ArcNode1; 

//顶点表结点 
typedef struct VNode{ 
	VertexType data; //顶点信息 
	int in; //入度 
	ArcNode *firstarc; //指向第一条依附该顶点的弧的指针 }VNode,AdjList[MAX_VERTEX_NUM]; 
	
typedef struct{ 
	AdjList vertices; //存储头结点的数组 
	int vexnum,arcnum; //图的当前顶点数和弧数 
}ALGraph; 

typedef struct{ //栈 
	SElemType *base; 
	SElemType *top; 
	int stacksize; 
	int size;
}SqStack; 

int LocateVex(ALGraph *G,char v){ 
	int i=0; 
	for(;i<G->vexnum;i++){ 
		if(G->vertices[i].data==v) 
			return i; 
			}
			if(i>=G->vexnum) 
				return -1; 
			else
				return 0; 
}

//创建图
void CreateGraph(ALGraph *G){ 
	int i,j,k,b1; 
	char a1,a2,a3; //a2 接收换行符 
	ArcNode *p; //边表结点 
	printf("请输入顶点数和弧数:\n"); 
	scanf("%d%d",&G->vexnum,&G->arcnum); 
	printf("\n 输入顶点信息:\n"); 
	for(i=0;i<G->vexnum;i++){ 
		getchar(); 
		scanf("%c",&G->vertices[i].data); 
		G->vertices[i].firstarc=NULL; //初始化指针 
		G->vertices[i].in=0; //初始化入度 
	}
	printf("\n 请输入弧头和弧尾和权值:\n"); 
	for(k=0;k<G->arcnum;k++){ 
		getchar(); 
		scanf("%c%c%c%d",&a1,&a2,&a3,&b1); 
		i=LocateVex(G,a1); 
		j=LocateVex(G,a3); 
		p=(ArcNode *)malloc(sizeof(ArcNode)); 
		p->adjvex=j; 
		p->weight=b1; 
		p->nextarc=G->vertices[i].firstarc; 
		G->vertices[i].firstarc=p; 
	} 
}

//计算每个点的入度
void FindInDegree(ALGraph *G){ 
	int a; 
	ArcNode1 p; 
	a=G->vexnum; 
	int bs[MAX_VERTEX_NUM]; 
	for(int i=0;i<a;i++){ 
		bs[i]=0; //保存每个结点的入度 
	}
	for(int i=0;i<a;i++){ 
		p=G->vertices[i].firstarc; 
		while(p!=NULL){ 
			bs[p->adjvex]++; 
			p=p->nextarc; 
		} 
	}
	for(int i=0;i<a;i++){ //将入度存入结点中 
		G->vertices[i].in=bs[i];
	} 
}

//创建栈 
int InitStack(SqStack *s){ 
	s->base=(SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType)); 
	if(!s->base) 
		exit(0); 
	s->top=s->base; 
	s->stacksize=STACK_INIT_SIZE; s->size=0; 
	return 1; 
}

//入栈 
int Push(SqStack *s,SElemType e){ 
	if(s->top-s->base>=s->stacksize){ 
		s->base=(SElemType *)realloc(s->base,(s->stacksize+STACK_INIT_S IZE)*sizeof(SElemType)); 
		if(!s->base) 
			exit(0); 
		s->top=s->base+s->stacksize; 
		s->stacksize+=STACKINCREMENT; 
	}
	*(s->top++)=e; //若栈不为空,用 e 返回其值 
	s->size++; 
	return 1; 
}

//出栈 
int Pop(SqStack *s,SElemType *e){ 
	if(s->top==s->base) 
		return 0; 
	*e=*--(s->top); 
	s->size--; 
	return 1; 
}

void GetTop(SqStack *s,SElemType *e){ 
	if(s->top==s->base) 
		return; 
	*e=*(s->top-1); 
}

//判空
int StackEmpty(SqStack *s){  
	if(s->base==s->top){ 
		return 1; 
	}else{
	return 0; 
	} 
}

//拓扑排序
int TopologicalOrder(ALGraph *G,SqStack *T){  
	ArcNode1 p; 
	int count,e,k; 
	FindInDegree(G); 
	SqStack S; 
	InitStack(&S); 
	for(int j=0;j<G->vexnum;j++){ 
		if(G->vertices[j].in==0){ 
			Push(&S,j); 
		} 
	}
	count=0; 
	for(int i=0;i<G->vexnum;i++) //初始化 
		ve[i]=0; 
	while(!StackEmpty(&S)){ 
		Pop(&S,&e); 
		Push(T,e); 
		count++; 
		for(p=G->vertices[e].firstarc;p;p=p->nextarc){ 
			k=p->adjvex;
			G->vertices[k].in--; 
			if(G->vertices[k].in==0) 
				Push(&S,k); 
			if(ve[e]+p->weight>ve[k]){ 
				ve[k]=ve[e]+p->weight; 
			} 
		} 
	}
	if(count<G->vexnum) 
		return 0; 
	else
		return 1; 
}

void PrintPath(ALGraph *G,SqStack *T){ 
	for(int i=0;i<T->size;i++) 
		printf("%c ",G->vertices[T->base[i]].data); 
}

//DFS查找关键路径
void DFS(ALGraph *G,int v,SqStack *T){ 
	int e,i; 
	GetTop(T,&e); 
	if(xs[e][v]==1) Push(T,v); 
	else
		return; 
	visited[v]=2; //标记访问过 
	ArcNode *p; 
	for(i=0,p=G->vertices[v].firstarc;p;p=p->nextarc,i++){
		if(visited[p->adjvex]==1)	 				
			DFS(G,p->adjvex,T); 
		}
		if(!p&&i==0){ 
			PrintPath(G,T); 
			printf("\n"); 
		}
		Pop(T,&e); 
		visited[e]=1; 
}

void DFSTraverse(ALGraph *G){
	ArcNode *p; 
	SqStack T; 
	InitStack(&T); 
	int i; 
	for(i=0;i<G->vexnum;i++){ 
		if(visited[i]==1){ 
			Push(&T,i); 
			visited[i]=2; 
			break; 
		} 
	}
	for(p=G->vertices[i].firstarc;p;p=p->nextarc){ 
		if(visited[p->adjvex]==1) 
		DFS(G,p->adjvex,&T); 
	} 
}

//关键路径 
void Criticalpath(ALGraph *G,SqStack *T){ 
	int e,k,dut,ee,el,k1=0,x1=100,n1=0; 
	ArcNode1 p; 
	if(!TopologicalOrder(G,T)){ 
		printf("有向网存在回路!"); 
		return; 
	}
	for(int i=0;i<G->vexnum;i++) 
		for(int j=0;j<G->vexnum;j++) 
			xs[i][j]=0; //初始化 
	for(int a=0;a<G->vexnum;a++) 
		vl[a]=ve[G->vexnum-1]; //初始化 
	while(!StackEmpty(T)){ 
		Pop(T,&e); 
		for(p=G->vertices[e].firstarc;p;p=p->nextarc){ 
			k=p->adjvex; 
			dut=p->weight; 
			if(vl[k]-dut<vl[e]) 
				vl[e]=vl[k]-dut; 
		} 
	}
	for(int i=0;i<G->vexnum;i++){ //初始化 
		visited[i]=0; 
	}
	printf("\n 关键活动:"); 
	for(int j=0;j<G->vexnum;j++){
		for(p=G->vertices[j].firstarc;p;p=p->nextarc){ 
			k=p->adjvex; 
			dut=p->weight; 
			ee=ve[j]; 
			el=vl[k]-dut; 
			if(ee==el){ 
				visited[j]=1; //DFS 标记数组 
				visited[k]=1; xs[j][k]=1; //标记路径 
				xs[k][j]=1; 
				printf("\n %c->%c",G->vertices[j].data,G->vertices[ k].data); 
			} 
		} 
	}
	printf("\n 最少花费时间为:%d \n",ve[G->vexnum-1]); 
	printf("关键路径:\n"); 
	DFSTraverse(G); 
}

int main(){ 
	int LocateVex(ALGraph *G,char v); //定位 
	void CreateGraph(ALGraph *G); //创建有向网 
	void FindInDegree(ALGraph *G); //求入度 
	int InitStack(SqStack *s); //创建栈 
	int Push(SqStack *s,SElemType e); //压栈 
	int Pop(SqStack *s,SElemType *e); //出栈 
	void GetTop(SqStack *s,SElemType *e); //获取栈顶元素 
	int StackEmpty(SqStack *s); //判断栈空 
	int TopologicalOrder(ALGraph *G,SqStack *T); //拓扑排序 
	void PrintPath(ALGraph *G,SqStack *T); //栈元素输出 
	void DFS(ALGraph *G,int v,SqStack *T); //深度优先搜索 
	void DFSTraverse(ALGraph *G); 
	void Criticalpath(ALGraph *G,SqStack *T); //关键路径 
	ALGraph G; 
	SqStack T; 
	CreateGraph(&G); //创建邻接表 
	InitStack(&T); //创建栈 
	Criticalpath(&G,&T); //关键路径 
	printf("\n"); 
	system("pause"); 
	return 0; 
}

结果
数据结构——关键路径(C语言)_第1张图片

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