算法:图的关键路径-数据结构(24)

一、个人理解-简单说说

关键路径其实就是一个点到另外一个点的最唱路径,其中这个图是有向无环图。求出工程里面最关键的事件,不能拖延的事件路径,参考P183-P186。解决的思路,便是找出每个事件点的最早开始时间Ve和最晚完成时间ee,当Ve==ee的情况下就是不能拖延的事件,便是关键路径。

二、数据结构与算法

1、有向图的十字链表 

//==========有向图的十字链表===============
typedef int InfoType;
typedef struct ArcBox{
	int tailvex, headvex;//数组索引的弧头和弧尾
	struct ArcBox *hlink, *tlink;//头链和尾链 头链是弧头相同的下一条弧 尾弧是弧尾相同的一条弧
	InfoType info;
}ArcBox;//弧

typedef struct VexNode{
	VertexType data;
	ArcBox *firstin, *firstout;
}VexNode;//头结点

typedef struct{
	VexNode xlist[MAX_VERTEX_NUM];
	int vexnum, arcnum;//顶点总数 和弧总数
}OLGraph;

Status CreateDG(OLGraph &G, int vexnum, char * vexs, int arcnum, int maps[][6]){
	//创建有向图
	G.vexnum = vexnum;
	G.arcnum = arcnum;
	for (int i = 0; i < G.vexnum; i++)
	{
		//初始化指针
		G.xlist[i].data = vexs[i];
		G.xlist[i].firstin = NULL;
		G.xlist[i].firstout = NULL;
	}
	for (int i = 0; i < vexnum; i++)
	{
		for (int j = 0; j < vexnum; j++)
		{
			if (maps[i][j] != 0)
			{
				//i到j这个有有弧
				ArcBox * p = (ArcBox*)malloc(sizeof(ArcBox));
				
				*p = { i, j, G.xlist[j].firstin, G.xlist[i].firstout, maps[i][j] };
				G.xlist[i].firstout = p;
				G.xlist[j].firstin = p;
			}
		}
	}
	return OK;
}
//对弧的所有遍历.
void PrintfGraphDg(OLGraph G){
	printf("弧尾\t弧头\n");
	for (int i = 0; i < G.vexnum; i++)
	{
		ArcBox* arc = G.xlist[i].firstout;
		while (arc!=NULL)
		{
			printf("%d\t%d \t%c->%c 权重:%d\n", arc->tailvex, arc->headvex, G.xlist[arc->tailvex].data, G.xlist[arc->headvex].data,arc->info);
			arc = arc->tlink;
		}
	}
}

2、关键路径算法

//利用拓扑排序算法计算ve数组
int ve[MAX_VERTEX_NUM];//点的最早完成时间数组
int vl[MAX_VERTEX_NUM];//点的最迟完成时间数组
Status TopologicalOrder(OLGraph G,SqStack &T){
	//初始化ve数组
	for (int i = 0; i < G.vexnum; i++)
	{
		ve[i] = 0; 
	}
	int indegree[MAX_VERTEX_NUM];//存放各顶点入度的数组
	//生成入度数组
	for (int i = 0; i < G.vexnum; i++)
	{
		int count = 0;
		ArcBox* arc = G.xlist[i].firstin;//入度的弧
		while (arc != NULL)
		{
			count++;
			arc = arc->hlink;
		}
		indegree[i] = count;
		printf("%c的入度为%d \n", G.xlist[i].data, indegree[i]);
	}

	SqStack stack;//保存的是入度为0的结点
	InitStack(stack);
	InitStack(T);//初始化栈
	for (int i = 0; i < G.vexnum; i++)
	{
		if (indegree[i] == 0)
		{
			//入度为0 进栈
			//printf("push %d\n", i);
			Push(stack, i);
		}
	}

	int count = 0;//计数计算拓扑排序入度为0 的个数
	while (!StackEmpty(stack)){
		//在栈不为空的情况下
		int elem;
		Pop(stack, elem);
		//获取逆的拓扑排序
		Push(T, elem);

		count++;
		printf("pop %d\n", elem);
		//printf("%c", G.xlist[elem].data);

		//遍历所有指向elem顶点的弧
		ArcBox* arc = G.xlist[elem].firstout;//出去的弧
		while (arc != NULL)
		{
			int k = arc->headvex;//当前弧的头的索引
			if ((--indegree[k]) == 0)
			{
				//printf("push %d\n", arc->tailvex);
				Push(stack, k);
			}
			//找出j-k的弧上最大的边
			if (ve[elem] + arc->info > ve[k])
			{
				ve[k] = ve[elem] + arc->info;
			}
			arc = arc->tlink;
		}
	}
	if (count < G.vexnum)
	{
		printf("个图有回路");
		return ERROR;//这个图有回路
	}


	return OK;
}
//求出关键路径
Status CriticalPath(OLGraph G){
	SqStack T;
	TopologicalOrder(G, T);
	for (int i = 0; i < G.vexnum; i++)
	{
		//printf("%d:",ve[i]);
	}
	//计算出vl数组
	//初始化vl数组 最后一个点的ve与vl相等
	for (int i = 0; i < G.vexnum; i++)
	{
		vl[i] = ve[G.vexnum - 1];
	}
	//使用逆拓扑排序计算出所有的vl
	while (!StackEmpty(T))
	{
		int j;//当前计算的点j
		Pop(T, j);
		//遍历点j的所有
		ArcBox* arc = G.xlist[j].firstout;//出去的弧
		while (arc != NULL)
		{
			int k = arc->headvex;//当前弧的头的索引
			//找出j-k的弧上最大的边
			if (vl[k] - arc->info < vl[j])
			{
				vl[j] = vl[k] - arc->info;
			}
			arc = arc->tlink;
		}
	}

	//for (int i = 0; i < G.vexnum; i++)
	//{
	//	printf("%d:", vl[i]);
	//}

	//求出ee和el 就是关于弧的最早开始时间和最晚开始时间 遍历所有的弧
	for (int i = 0; i < G.vexnum; i++)
	{
		ArcBox* arc = G.xlist[i].firstout;
		while (arc != NULL)
		{
			//printf("%d\t%d \t%c->%c 权重:%d\n", arc->tailvex, arc->headvex, G.xlist[arc->tailvex].data, G.xlist[arc->headvex].data, arc->info);
			int ee = ve[arc->tailvex];
			int el = vl[arc->headvex] - arc->info;
			if (ee == el)
			{
				printf("%d\t%d \t%c->%c 权重:%d\n", arc->tailvex, arc->headvex, G.xlist[arc->tailvex].data, G.xlist[arc->headvex].data, arc->info);
			}
			arc = arc->tlink;
		}
	}
	return OK;
}

三、执行

	//链表创建进行拓扑排序和关键路径
	int vexnum = 6;
	char *vexs = "abcdef";
	int arcnum = 8;
	int maps[6][6] = {
		{ 0, 3, 2, 0, 0, 0 },
		{ 0, 0, 0, 2, 3, 0 },
		{ 0, 0, 0, 4, 0, 3 },
		{ 0, 0, 0, 0, 0, 2 },
		{ 0, 0, 0, 0, 0, 1 },
		{ 0, 0, 0, 0, 0, 0 }
	};
	OLGraph G;
	CreateDG(G,vexnum,vexs,arcnum,maps);
	//PrintfGraphDg(G);
	//DFSTraverse_L(G);
	//BFSTraverse_L(G);
	//TopologicalSort(G);//拓扑排序
	CriticalPath(G);//关键路径

输出:
a的入度为0
b的入度为1
c的入度为1
d的入度为2
e的入度为1
f的入度为3
pop 0
pop 1
pop 4
pop 2
pop 3
pop 5
0       2       a->c 权重:2
2       3       c->d 权重:4
3       5       d->f 权重:2
请按任意键继续. . .






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值