数据结构图之关键路径与拓扑排序的实现

文章目录


算法实现

#include<iostream>
#include<stack>

using namespace std;
typedef int Vertextype;
typedef struct ArcNode
{
	int index;
	int weight;
	struct ArcNode* next;
}ArcNode;
typedef struct VNode
{
	Vertextype data;
	int indegree = 0;
	ArcNode* firstAdj;
}VNode;
//生成图
void CreateGraph(VNode G[], int n);				//n 顶点数
void TopSort(VNode G[], int n);
//void Cr(VNode G[], int n, int e);
//int main()
//{
//
//	VNode G[100];
//	int n;
//	cin >> n;
//	CreateGraph(G, n);
//	TopSort(G, n);
//	/*
//	9
//	1 6 2 4 3 5 -1
//	4 1 -1
//	4 1 -1
//	5 2 -1
//	6 9  7 7 -1
//	7 4 -1
//	8 2 -1
//	8 4 -1
//	-1
//	<0,1> lenth: 6
//	<1,4> lenth: 1
//	<4,6> lenth: 9
//	<4,7> lenth: 7
//	<6,8> lenth: 2
//	<7,8> lenth: 4
//	一组供测试的数据
//	*/
//	return 0;
//}
// 
// 
// 
//生成图
/*
录入顶点和边数据
*/
void CreateGraph(VNode G[], int n)//n 顶点数
{
	for (int i = 0; i < n; i++)
	{
		int v;
		G[i].firstAdj = NULL;
		G[i].data = i;
		ArcNode* p = NULL, * q = NULL;
		cin >> v;
		while (v != -1)
		{
			p = new ArcNode;
			p->index = v;
			p->next = NULL;
			cin >> p->weight;
			G[v].indegree += 1;
			if (G[i].firstAdj == NULL)
			{
				G[i].firstAdj = p;
			}
			else
			{
				q->next = p;
			}
			cin >> v;
			q = p;
		}
	}
}

// 关键路径适用于AOE网,即用边表示活动,顶点表示事件
// 
//拓扑排序求关键路径
void TopSort(VNode G[], int n)
{
	int* ETV = new int[n];			//事件最早发生时间
	/*
	* 一个事件可以开始进入到活动,需要它的前置条件都完成,因此事件的最早发生时间是从
	* 源点到此点最长的权值路径,因此既要满足拓扑排序又要求路径最大值
	*/
	int* LTV = new int[n];			//事件最晚发生时间
	/*
	* 不影响整个工程该事件的最晚发生时间,即 用它下一个事件 的 最晚发生时间 减去 它完成两个事件间的活动
	* 权值所得到的结果
	*/
	int* Ans = new int[n];			//记录拓扑排序节点序列
	int count = 0;					//记录已拓扑排序的节点数,比n小说明该图中存在环
	stack<int> s;					//初始化一个栈来进行拓扑排序
	for (int i = 0; i < n; i++)
	{
		ETV[i] = 0;
		LTV[i] = 0;
		if (G[i].indegree == 0)		//遍历图的节点,找到入度为0的那个源点加入到栈中
			s.push(i);
	}
	int p = 0;
	while (!s.empty())
	{
		p = s.top();				//出栈获得节点
		s.pop();
		Ans[count++] = p;			//栈内的节点肯定是入度为0的节点,因此直接加入到拓扑序列中存储
		ArcNode* q = G[p].firstAdj;	//q用于遍历p节点的所有邻接点
		while (q != NULL)
		{
			/*
			拓扑排序是通过从图中去掉该入度为0的节点,并将所有以该节点为起始节点的边去掉,
			因此直接将该节点连接的另外节点的入度减一就可以到达等效的目的
			*/
			G[q->index].indegree -= 1;	//邻接点的入度减一
			if (ETV[p] + q->weight > ETV[q->index])	//如果p的最早发生时间加上完成该边上的活动权值大于
			{										//邻接点q的最早发生时间,就进行时间更新,保证最早发生时间是最长的路径
				ETV[q->index] = ETV[p] + q->weight;
			}
			if (G[q->index].indegree == 0)//如果邻接点入度减1后入的变为了0,就将该邻接点入栈用于下一论拓扑排序
			{
				s.push(q->index);
			}
			q = q->next;				//将q赋为下一个邻接点
		}
	}
	/*
	AOE网的性质,最后一个拓扑序列的点必定是汇点,所对应的最早发生时间就是完成该工程的时间
	*/
	for (int i = 0; i < n; i++)
		LTV[i] = ETV[n - 1];					//将最晚发生时间都初始化为完成工程的时间
	for (int i = n - 2; i >= 0; i--)			//这里从汇点前面一个节点往初始节点开始计算,因为汇点的最早发生时间和最晚发生时间都是完成工程的时间
	{
		ArcNode* p = G[i].firstAdj;				//p是节点G[i]的邻接点
		while (p != NULL)
		{
			if (LTV[G[i].data] > LTV[p->index] - p->weight) //邻接点的最晚发生时间减去两事件间活动的权值即为最晚发生事件
			{/*
			 另外 这里为什么是进行小于比较,我想的是:规定一个事件最晚要在6号开始动工,那么你就不能拖到7号开始,那样就延误了时间
			 所以这里是进行小于比较
			 */
				LTV[G[i].data] = LTV[p->index] - p->weight; //如果小于就进行最晚发生事件的更新
			}
			p = p->next;								//进入到下一个邻接点
		}
	}
	int ete, lte;						//ete是活动最早发生时间,lte是活动最晚发生时间
	/*
	活动的最早发生时间是与它前面的时间的最早发生时间相同,事件的发生就代表事件后面的活动可以开始了。

	活动最晚发生时间就是它后面事件的最晚发生时间减去活动的权值,这就是活动最晚发生时间了。如:现在有事件u,v
	u,v间的活动是<u,v>,活动权值假设为5,整个工程时间是18,而v的最晚发生时间是10,因此要想v不延迟,那么活动<u,v>
	最晚要在10的时候结束,因此要在10-5时候开始,即<u,v>的最晚发生时间是10-5
	*/
	for (int i = 0; i < n; i++)
	{
		ArcNode* p = G[i].firstAdj;			//p是事件i的邻接点
		while (p != NULL)
		{
			ete = ETV[i];					//ete是活动最早发生时间,因此与事件i的最早发生时间相同
			lte = LTV[p->index] - p->weight;//lte是活动最晚发生时间,因此找到i的邻接点的最晚发生时间并减去他们直接活动所需的权值可求得
			if (ete == lte)					//关键路径的特点是最早发生时间和最晚发生时间相同的活动,如果该活动的ete与lte相等即表明
			{								//该活动是关键路径
				cout << "<" << i << "," << p->index << "> lenth: " << p->weight << endl;
			}
			p = p->next;					//p赋为下一个邻接点
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值