拓扑排序和关键路径算法 (C语言实现)

拓扑排序

首先要说明一下,拓扑排序不是一种排序方式,而是做一系列事件的可行次序。我们日常生活中,有时必须先完成一些事情,然后才能做另外一些事情。举个例子,我们学数学的时候,一定是先学习加减法,然后学乘除法,接着学习平方、根号等。不可能反着学。这就是拓扑排序。
拓扑排序可能是不唯一的,例如学完加减法之后,可以先学乘法,也可以先学除法。
拓扑排序不可能循环依赖 (存在环),例如在完成A事件之前必须完成B事件,在完成B事件之前必须完成A事件。如果发生这样的情况,那么这些事件不可能组成拓扑排序。如果在计算机运行的时候发生这样的情况,计算机就会被锁死。

好了,我们把这个问题抽象成“有向图”。对于有向图,只能输出最顶级的结点 (入度为0,没有上一级的顶点),输出后删除这个节点,一直循环,直到所有点都被输出,这就是一个拓扑排序。拓扑排序不可能存在环,因为环上的结点没有入度为0的点。

我们怎么计算一个有向图的拓扑排序呢?通过上述描述,其实已经很清楚了,步骤为:
1、找到有向图中的所有入度为0的结点;
2、输出入度为0的结点,同时把这些结点的下一级结点的入度 - 1。
3、重复2,直到所有点都输出。
4、判断是不是存在拓扑排序。如果输出的结点个数少于总结点数,则说明图中有环,这个图不含有拓扑排序。

可以考虑用栈或队列辅助生成拓扑排序。用栈辅助生成拓扑排序时,左下图的输出结果为:2, 0, 1, 3, 4, 5, 6, 7。而用队列辅助生成拓扑排序时,左下图的输出结果为:0, 2, 1, 3, 4, 6, 5, 7。从这里我们也可以看出一个有向图可能存在多个拓扑排序。

在这里插入图片描述
我们先用数组实现的栈辅助生成拓扑排序:
step 1:找到有向图中的所有入度为0的结点:,并把编号存放在stack中

for(i = 0; i < size; i++) 
{
   
	struct LinkNode * node = arrNode[i].next;
	struct ArrayNode myArrayNode = arrNode[i];
	if(myArrayNode.inDegree == 0)
	{
   
		stack[top] = i; //把结点的编号存放到stack中 
		top++; //top + 1 
	}
}

step 2:输出入度为0的结点,同时把这些结点的下一级结点的入度 - 1。

top--; //top的位置没有内容,所以要先 - 1 
index = stack[top]; //得到存放在stack中的编号 
printf("%d -> ", index); //输出编号 
count++; //计数 + 1 

//从arrNode中获得结点的指针 
struct LinkNode * node = (arrNode[index]).next;

//遍历
while(node != NULL) //如果子节点不是NULL就循环 
{
   
	int sonIndex = node->index; //子节点的index 
	
	//从arrNode中获得结点、结点信息 
	if(arrNode[sonIndex].inDegree > 0) //子节点的入度 > 0 
	{
   
		//结点的inDegree - 1 
		arrNode[sonIndex].inDegree--;
		
		//如果结点的inDegree == 0
		if(arrNode[sonIndex].inDegree == 0)
		{
   
			//把结点的index (也就是sonIndex) 加入到stack中 
			stack[top] = sonIndex;
			top++;
		}
	}
	node = node->next
  • 6
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值