关键路径(C语言简单实现)
本文参考自《大话数据结构》,对关键路径的理解参考自:https://www.jianshu.com/p/1857ed4d8128
在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,用边上的权值表示活动的持续时间,这种有向图的边表示活动的网,我们称之为AOE
网 。我们把AOE
网中没有入边的顶点称为始点或源点,没有出边的顶点称为终点或汇点。
尽管AOE
网和AOV
网都是用来对工程建模的,但它们还是有很大的不同,主要体现在AOV
网是顶点表示活动的网,只描述活动之间的制约关系,而AOE
网是用边表示活动的网,边上的权值表示活动持续的时间。
关键路径算法原理
为了讲清楚求关键路径的算法,我还是来举个例子。假设一个学生放学回家,除掉吃饭、洗漱外,到睡觉前有四小时空闲,而家庭作业需要两小时完成。不同的学生会有不同的做法,抓紧的学生,会在头两小时就完成作业,然后看看电视、读读课外书什么的;但也有超过一半的学生会在最后两小时才去做作业,要不是因为没时间,可能还要再拖延下去。
这里做家庭作业这活动的最早开始时间是四小时的开始,可以理解为0,而最晚开始时间是两小时之后马上开始,不可以再晚,否则就是延迟了,此时可以理解为2。显然,当最早和最晚开始时间不相等时就意味着空闲。
接着,你老妈发现了你拖延的小秘密,于是买了很多的课外习题,要求你四个小时,不许有一丝空闲,省得你拖延或偷懒。此时整个四小时全部被占满,最早开始时间和最晚开始时间都是0,因此它就是关键活动了。
也就是说,我们只需要找到所有活动的最早开始时间和最晚开始时间,并且比较它们,如果相等就意味着此活动是关键活动,活动间的路径为关键路径。如果不等,则就不是。
我们把路径上各个活动所持续的时间之和称为路径长度,从源点到汇点具有最大长度的路径叫关键路径,在关键路径上的活动叫关键活动 。
几个关键参数
- 事件的最早发生时间
etv
:即顶点vk
的最早发生时间; - 事件的最晚发生时间
ltv
:即顶点vk
的最晚发生时间,也就是每个顶点对应的事件最晚需要开始的时间,超出此时间将会延误整个工期; - 活动的最早开工时间
ete
:即弧ak
的最早发生时间; - 活动的最晚开工时间
lte
:即弧ak
的最晚发生时间,也就是不推迟工期的最晚开工时间。
可以由1和2求的3和4,然后再根据ete[k]
是否与lte[k]
相等来判断ak
是否是关键活动
关键路径算法
与拓扑排序时邻接表结构不同的地方在于,弧链表增加了weight
域,用来存储弧的权值。
求事件的最早发生时间etv
的过程,就是我们从头到尾找拓扑的过程,因此,在求关键路径之前,需要先调用一次拓扑序列算法的代码来计算etv
和拓扑序列列表。为此,我们先在程序开始处声明几个全局变量。
int *etv, *ltv; //事件最早发生时间和最迟发生时间数组
int *stack2; //用于存储拓扑序列的栈
int top2; //用于stack2的指针
下面是改进过的求拓扑序列算法:
Status TopologicalSort(GraphAdjList GL){
EdgeNode *e;
int i, k, gettop;
int top = 0; //用于栈指针下标
int count = 0; //用于同级输出顶点的个数
int *stack; //建栈将入度为0的顶点入栈
stack = (int*)malloc(GL->numVertexes * sizeof(int));
for(i=0;i<GL->numVertexes;i++)
if(0 == GL->adjList[i].in)
stack[++top] = i;
top2 = 0; //初始化为0
etv = (int*)malloc(GL->numVertexes*sizeof(int)); //事件最早发生时间
for(i=0;i<GL->numVertexes;i++)
etv[i] = 0; //初始化为0
stack2 = (int*)malloc(GL->numVertexes*sizeof(int)); //初始化
while(top!=0){
gettop=stack[top--];
count++;
stack2[++top2] = gettop; //将弹出的顶点序号压入拓扑序列的栈
for(e=GL->adjList[gettop].firstedge;e;e=e->next){
k = e->adjvex;
if(!(--GL->adjList