博客
关于我
图(三):拓扑排序、关键路径
阅读量:323 次
发布时间:2019-03-04

本文共 5154 字,大约阅读时间需要 17 分钟。

作为数据结构的课程笔记,以便查阅。如有出错的地方,还请多多指正!

注:C++忘得太厉害了。。算法先用C实现,等之后复习了再改成C++

目录

  • 有向无环图 / DAG图(directed acycline graph)

拓扑排序 Topological Sort

问题提出:学生选修课程问题
学生应按怎样的顺序学习这些课程,才能无矛盾、顺利地完成学业?

在这里插入图片描述

  • AOV网——用顶点表示活动,用弧表示活动间优先关系的有向图称为顶点表示活动的网(Activity On Vertex network)
    AOV网中不允许有回路,这意味着某项活动以自己为先决条件
  • 拓扑排序——把AOV网络中各顶点按照它们相互之间的优先关系排列成一个线性序列

拓扑排序的方法

  • 在有向图中选一个没有前驱的顶点且输出之
  • 从图中删除该顶点和所有以它为尾的弧
  • 重复上述两步,直至全部顶点均已输出;或者当图中不存在无前驱的顶点为止
    在这里插入图片描述

检测AOV网中是否存在环方法:对有向图构造其顶点的拓扑有序序列,若网中所有顶点都在它的拓扑有序序列中,则该AOV网必定不存在环
在这里插入图片描述

算法实现

拓扑排序中,最常用的操作就是求邻接点和入度。因此以邻接表作为存储结构

  • 额外设置一个数组来存储各个结点的入度,或者加入入度域
    在这里插入图片描述
  • 邻接表中所有入度为0的顶点进栈
  • 栈非空时,栈顶元素 V V V出栈并输出到拓扑序列中;将 V V V的所有邻接顶点入度减1,若为0则进栈
    重复上述操作直至栈空为止
  • 若栈空时输出的顶点个数不是 n n n,则有向图有环;否则,拓扑排序完毕
    在这里插入图片描述
    输出序列:6 1 3 2 4 5
static void Calc_indegree(pAdjacentList_t pgraph, int indegree[]){   	for (int i = 0; i < pgraph->vexNum; ++i)	{   		indegree[i] = 0;	}	for (int i = 0; i < pgraph->vexNum; ++i)	{   		pArcNode_t arc;		for (arc = pgraph->vex[i].firstEdge; arc; arc = arc->nextEdge)		{   			indegree[arc->head]++;		}	}}//拓扑排序int Topological_sort(pAdjacentList_t pgraph, void (*visit)(AdjacentListDataType_t data)){   	int* indegree = (int*)malloc(pgraph->vexNum * sizeof(int));	pStackNode_t ptop;	int cnt = 0;//输出的顶点个数	Calc_indegree(pgraph, indegree);	Stack_init(&ptop);	for (int i = 0; i < pgraph->vexNum; ++i)	{   		if (0 == indegree[i])		{   			Push(&ptop, i);		}	}		while (!Stack_is_empty(ptop))	{   		int i;		pArcNode_t arc;		Pop(&ptop, &i);		visit(pgraph->vex[i].data);		++cnt;		for (arc = pgraph->vex[i].firstEdge; arc; arc = arc->nextEdge)		{   			if (0 == --indegree[arc->head])			{   				Push(&ptop, arc->head);			}		}	}	free(indegree);	return (cnt == pgraph->vexNum);//是否有环}

T ( n ) = O ( n + e ) T(n)=O(n+e) T(n)=O(n+e)

关键路径 Critical Path

问题提出

设一个工程有11项活动,9个事件
问题:(1) 完成整项工程至少需要多少时间? (2) 哪些活动是影响工程进度的关键?

把工程计划表示为有向图。每个事件表示在它之前的活动已完成,在它之后的活动可以开始
在这里插入图片描述

在这里插入图片描述
只有在不改变关键路径的前提下,提高所有关键路径上的公共活动的效率才能缩短整个工期

基本概念

  • 完成整个工程的最短时间为:从有向图的源点到汇点的最长路径
  • AOE网(Activity On Edge)。AOE网中顶点表示事件,弧表示活动,权表示活动持续时间
  • 关键路径——路径长度最长的路径
  • V e ( j ) V_e(j) Ve(j)——表示事件 V j V_j Vj的最早发生时间
  • V l ( j ) V_l(j) Vl(j)——表示事件 V j Vj Vj的最迟发生时间(不影响工期)
  • e ( i ) e(i) e(i)——表示活动 a i a_i ai的最早开始时间
  • l ( i ) l(i) l(i)——表示活动 a i a_i ai的最迟开始时间(不影响工期)
  • l ( i ) − e ( i ) l(i)-e(i) l(i)e(i)——表示完成活动 a i a_i ai的时间余量
  • 关键活动—— l ( i ) = e ( i ) l(i)=e(i) l(i)=e(i)的活动,由关键活动组成关键路径

问题分析

  • 如何找 e ( i ) = l ( i ) e(i)=l(i) e(i)=l(i)的关键活动?
    设活动 a i a_i ai用弧 < j , k > <j,k> <j,k>表示,其持续时间记为: d u t ( < j , k > ) dut(<j,k>) dut(<j,k>)
    则有: e ( i ) = V e ( j ) e(i)=V_e(j) e(i)=Ve(j) l ( i ) = V l ( k ) − d u t ( < j , k > ) l(i)=V_l(k)-dut(<j,k>) l(i)=Vl(k)dut(<j,k>)

  • 如何求 V e ( j ) V_e(j) Ve(j) V l ( j ) V_l(j) Vl(j)
    (1) 从 V e ( 1 ) = 0 V_e(1)=0 Ve(1)=0开始从前向后递推(拓扑有序)
    在这里插入图片描述
    (2)从 V l ( n ) = V e ( n ) V_l(n)=V_e(n) Vl(n)=Ve(n)开始从后向前递推(逆拓扑有序)
    在这里插入图片描述

  • 求关键路径步骤
    V e ( i ) V_e(i) Ve(i)
    V l ( j ) V_l(j) Vl(j)
    e ( i ) = V e ( j ) e(i) =V_e(j) e(i)=Ve(j)
    l ( i ) = V l ( k ) − d u t ( < j , k > ) l(i) =V_l(k)-dut(<j,k>) l(i)=Vl(k)dut(<j,k>)
    计算 l ( i ) − e ( i ) l(i)-e(i) l(i)e(i)

算法实现

  • 使用邻接表,增设入度域(或者一个记录各个结点入度的数组)
  • 计算每个顶点的入度
  • 对各个顶点进行拓扑排序,排序过程中求 V e ( i ) V_e(i) Ve(i)。将得到的拓扑序列入栈,以便之后得到逆拓扑序列
  • 按逆拓扑序列求 V l ( i ) V_l(i) Vl(i)
  • 计算每条弧(活动)的 e [ i ] e[i] e[i] l [ i ] l[i] l[i],找出 e [ i ] = l [ i ] e[i]=l[i] e[i]=l[i]的关键活动
//求图上的关键路径Status_e Critical_path(pAdjacentList_t pgraph){   	int* indegree = (int*)malloc(pgraph->vexNum * sizeof(int));	AdjacentListWeightType_t* ve = (AdjacentListWeightType_t*)malloc(pgraph->vexNum * sizeof(AdjacentListWeightType_t));	AdjacentListWeightType_t* vl = (AdjacentListWeightType_t*)malloc(pgraph->vexNum * sizeof(AdjacentListWeightType_t));	AdjacentListWeightType_t ee;	AdjacentListWeightType_t el;	pStackNode_t ptop;	int cnt = 0;//输出的顶点个数	pStackNode_t ptop_topological;//记录拓扑排序的次序 方便之后按逆序操作	int finish_time = 0;		Calc_indegree(pgraph, indegree);	Stack_init(&ptop);	Stack_init(&ptop_topological);	//入度为0的点入栈	for (int i = 0; i < pgraph->vexNum; ++i)	{   		if (0 == indegree[i])		{   			Push(&ptop, i);		}		ve[i] = 0;		vl[i] = INFINITY;	}	//栈不为空时,拓扑排序	while (!Stack_is_empty(ptop))	{   		int i;		pArcNode_t arc;		Pop(&ptop, &i);		Push(&ptop_topological, i);		++cnt;		for (arc = pgraph->vex[i].firstEdge; arc; arc = arc->nextEdge)		{   			ve[arc->head] = MAX(ve[i] + arc->weight, ve[arc->head]);			if (0 == --indegree[arc->head])			{   				Push(&ptop, arc->head);			}			finish_time = MAX(finish_time, ve[arc->head]);		}	}	if (cnt < pgraph->vexNum)	{   		printf("There is loop in the graph!\r\n");		return err;	}	else {   		printf("Project time:%d\n", finish_time);	}	while (!Stack_is_empty(ptop_topological))	{   		int i;		pArcNode_t arc;		Pop(&ptop_topological, &i);		arc = pgraph->vex[i].firstEdge;		if (NULL == arc)		{   			vl[i] = finish_time;		}		for (; arc; arc = arc->nextEdge)		{   			vl[i] = MIN(vl[arc->head] - arc->weight, vl[i]);		}	}	printf("Critical path:\r\n");	for (int i = 0; i < pgraph->vexNum; ++i)	{   		pArcNode_t arc;		for (arc = pgraph->vex[i].firstEdge; arc; arc = arc->nextEdge)		{   			ee = ve[i];			el = vl[arc->head] - arc->weight;			if (ee == el)			{   				printf("%d -> %d ( weight: %d ; ee/el : %d )\r\n", i, arc->head, arc->weight, ee);			}		}	}	free(indegree);	free(ve);	free(vl);	return ok;}

T ( n ) = O ( n + e ) T(n)=O(n+e) T(n)=O(n+e)

转载地址:http://caih.baihongyu.com/

你可能感兴趣的文章
C语言的数值溢出问题(上)
查看>>
函数指针的典型应用-计算函数的定积分(矩形法思想)
查看>>
8051单片机(STC89C52)以定时器中断模式实现两倒计时器异步计时
查看>>
用 wxPython 打印你的 App
查看>>
vue项目通过vue.config.js配置文件进行proxy反向代理跨域
查看>>
android:使用audiotrack 类播放wav文件
查看>>
vue通过better-scroll 封装自定义的下拉刷新组件
查看>>
android解决:使用多线程和Handler同步更新UI
查看>>
Element UI 中动态路由的分析及实现
查看>>
杭电 2007 平方和与立方和(输入数据的大小顺序并不能默认)
查看>>
十大排序算法之三:插入排序(Python)
查看>>
合并两个有序数组
查看>>
聊聊我的五一小假期
查看>>
CSS position属性static/relative/absolute/fixed/sticky用法总结
查看>>
6.14编一个程序,将两个字符串s1和s2比较,不要用strcmp函数。
查看>>
Java纯文本文件显示工具制作
查看>>
三、案例:留言板 & url.parse()
查看>>
LeetCode:28. 实现 strStr()——————简单
查看>>
Lionheart万汇:布林线双底形态分析技巧
查看>>
Vue使用bus进行组件间、父子路由间通信
查看>>