拓扑排序及关键路径


const int MaxVertexNum = 50;

struct edgenode
{
	int adjvex;  //邻接点
	int weight;
	edgenode *next;  //指向下一个边结点的链域
};

typedef edgenode *adjlist[MaxVertexNum];

/*
把这种顶点表示活动、边表示活动间先后关系的有向图称做顶点活动网
(Activity On Vertex network ),简称AOV网。
在AOV网中,若不存在回路,则所有活动可排列成一个线性序列,使得每个活动的所
有前驱活动都排在该活动的前面,我们把此序列叫做拓扑序列(Topological order),
由AOV网构造拓扑序列的过程叫做拓扑排序(Topological sort )。
AOV网的拓扑序列不是唯一的,满足上述定义的任一线性序列都称做它的拓扑序列。
*/

/*
由AOV网构造出拓扑序列的实际意义是:如果按照拓扑序列中的顶点次序,在开始每一项活动时,
能够保证它的所有前驱活动都已完成,从而使整个工程顺序进行,不会出现冲突的情况。
由AOV网构造拓扑序列的拓扑排序算法主要是循环执行以下两步,直到不存在入度为0的顶点为止。
	(1)选择一个入度为0的顶点并输出之;
	(2)从网中删除此顶点及所有出边。
循环结束后,若输出的顶点数小于网中的顶点数,则输出“有回路”信息,
否则输出的顶点序列就是一种拓扑序列。
*/

/*拓扑排序算法*/
#ifndef TOPOSORT

void Toposort(adjlist GL, int n)
	//对用邻接表GL表示的有向图进行拓扑排序
{
	int i,j,k,top,m = 0;  //m用来统计拓扑序列中的顶点数
	edgenode *p;
	//定义存储图中每个顶点入度的一维整型数组d
	int *d = new int[n];
	memset(d,0,sizeof(d));  //初始化数组d中的每个元素值为0
	//利用数组d中的对应元素统计出每个顶点的入度
	for (i = 0; i < n; i++)
	{
		p = GL[i];
		while (p != NULL)
		{
			j = p->adjvex;
			d[j]++;
			p = p->next;
		}
	}
	//初始化用于链接入度为0的元素的栈的栈顶指针top为-1
	top = -1;
	//建立初始化栈
	for(i = 0; i < n; i++)
	{
		if(d[i] == 0)
		{
			d[i] = top;
			top = i;
		}
	}
	//每循环一次删除一个顶点及所有出边
	while(top != -1)
	{
		j = top;   //j的值为一个入度为0的顶点序号
		top = d[top];  //删除栈顶元素
		cout<<j<<" ";   //输出一个顶点
		m++;   //输出的顶点加1
		p = GL[j];  //p指向vj邻接点表的第一个结点
		while (p != NULL)
		{
			k = p->adjvex;   //Vk是Vj的一个邻接点
			d[k]--;   //Vk的入度减1
			if (d[k] == 0)   //把入度为0的元素进栈
			{
				d[k] = top;
				top = k;
			}
			p = p->next;   //p指向Vj邻接点表的下一个结点
		}
	}
	cout<<endl;
	if(m < n)   //当输出的顶点数小于图中的顶点数时,输出有回路信息
		cout<<"The network has a cycle!"<<endl;
}

#endif

/*
AOE网(Activity On Edge network ),即边表示活动的网络。通常用它表示一个工程的计划或进度。

每个事件的最早发生时间应等于从源点到该顶点的所有路径上的最长路径长度

每个事件的最迟发生时间应等于汇点的最迟发生时间减去从该事件的顶点到汇点的最长路径长度(要倒着求出)

把开始时间余量(最迟开始时间-最早开始时间)为0的活动称为关键活动,
由关键活动所形成的从源点到汇点的每一条路径称为关键路径

*/

/*用邻接表GL表示一个AOE网的求关键路径的算法*/
#ifndef CRIPATH

void Toposort(adjlist GL, int v[], int n)
	//对用邻接表GL表示的有向图进行拓扑排序
{
	int i,j,k,top,m = 0;  //m用来统计拓扑序列中的顶点数
	edgenode *p;
	//定义存储图中每个顶点人度的一维整型数组d
	int *d = new int[n];
	memset(d,0,sizeof(d));  //初始化数组d中的每个元素值为0
	//利用数组d中的对应元素统计出每个顶点的入度
	for (i = 0; i < n; i++)
	{
		p = GL[i];
		while (p != NULL)
		{
			j = p->adjvex;
			d[j]++;
			p = p->next;
		}
	}
	//初始化用于链接入度为0的元素的栈的栈顶指针top为-1
	top = -1;
	//建立初始化栈
	for(i = 0; i < n; i++)
	{
		if(d[i] == 0)
		{
			d[i] = top;
			top = i;
		}
	}
	//每循环一次删除一个顶点及所有出边
	while(top != -1)
	{
		j = top;   //j的值为一个入度为0的顶点序号
		top = d[top];  //删除栈顶元素
		v[m] = j;
		m++;   //输出的顶点加1
		p = GL[j];  //p指向vj邻接点表的第一个结点
		while (p != NULL)
		{
			k = p->adjvex;   //Vk是Vj的一个邻接点
			d[k]--;   //Vk的入度减1
			if (d[k] == 0)   //把入度为0的元素进栈
			{
				d[k] = top;
				top = k;
			}
			p = p->next;   //p指向Vj邻接点表的下一个结点
		}
	}
	cout<<endl;
	if(m < n)   //当输出的顶点数小于图中的顶点数时,输出有回路信息
		cout<<"The network has a cycle!"<<endl;
}

void Cripath(adjlist GL, int n)
	//求邻接表GL表示的AOE网的关键路径
{
	int i,j,k;
	edgenode *p;
	//动态定义具有n个元素的三个一维整型数组V,Ve和Vl
	int  *v = new int[n];   //保存拓扑排序的结果
	int *ve = new int[n];   //保存每个事件的最早发生时间
	int *vl = new int[n];   //保存每个事件的最迟发生时间
	//调用拓扑排序算法,使排序结果存于数组V中
	Toposort(GL,v,n);
			//需对上一节介绍的此算法做必要的修改,即在参数表中
			//增加int v[]一项,把输出语句更换为v[m]=j即可
	memset(ve,0,sizeof(ve));  //给每个事件的最早发生时间置初值0
	//求出每个事件的最早发生时间
	for (i = 0; i < n; i++)
	{
		j = v[i];
		p = GL[j];
		while(p != NULL)
		{
			k = p->adjvex;   //k为邻接点
			if(ve[k] < ve[j] + p->weight)   //较大值
				ve[k] = ve[j] + p->weight;
			p = p->next;
		}
	}
	//把每个事件的最迟发生时间都置为ve[n-1],以作为它们的初值
	for(i = 0; i < n; i++)
		vl[i] = ve[n-1];
	//求出每个事件的最迟发生时间
	for (i = n-1; i >= 0; i--)
	{
		j = v[i];
		p = GL[j];
		while(p != NULL)
		{
			k = p->adjvex;  //k为邻接点
			if(vl[j] > vl[k] - p->weight)  //较小值
				vl[j] = vl[k] - p->weight;  
			p = p->next;
		}
	}
	//输出AOE网中每一个活动的最早开始时间,最迟开始时间,
	//以及开始时间余量
	for (i = 0; i < n; i++)
	{
		p = GL[i];   //把vi邻接点表的表头指针赋给p
		while(p != NULL)
		{
			j = p->adjvex;  //Vj是Vi的一个后继事件
			cout<<"<"<<i<<","<<j<<">";   //输出有向边<i,j>,用它表示该边上的活动ak
			cout<<ve[i]<<",";    //输出ak的最早开始时间
			cout<<vl[j] - p->weight<<",";   //输出ak的最迟开始时间
			cout<<vl[j] - p->weight - ve[i]<<endl;  //输出ak的开始时间余量
													//若此值为0,则所在边<i,j>为关键路径上的一条边
			p = p->next;
		}
	}
}

#endif



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值