news 2026/1/21 15:01:33

数据结构---关键路径

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
数据结构---关键路径

拓扑排序其实就是为了解决一个工程是否能够顺利解决的问题,但是我们在解决问题的时候往往需要考虑最短路径的问题,而最短路径在工程中往往不是费时最短时间所完成的路径,反而是最长时间的路线才是所需要的最短时间。就比如制造一辆汽车,造轮子很快但是制造外壳比较慢,那么等到汽车完全制造完毕的时间就取决于耗时最长造外壳的路线。

关键路径算法就是为了解决这个问题,与拓扑排序不同的是,拓扑排序的网中无权值也就是AOV网,而关键路径涉及权值也就是AOE网。

接下来讲解关键路径的原理,我们在下图中的每个顶点记为事件,每条弧边记为活动。实现关键路径就是让活动最早开工时间 ete (earliest time of edge)=活动最晚开工时间 lte (late time of edge),因为当活动的最早和最晚开工时间一致时说明这个活动是无法拖延的(也就是时间最长的,工程的完成与否由这几个活动决定), 而求活动的时间就要得到事件最早开始的时间etv (earlist time of vertex)事件最晚开始的时间 ltv (late time of vertex)

由于仍旧是在一个工程中所以我们求活动和事件的时间仍旧需要通过拓扑排序确定,简单的说,我们进行关键路径的步骤就是先正常的进行一遍拓扑排序找到每个顶点(事件)的最早开始的时间etv,然后再通过逆拓扑排序得到每个顶点(事件)最晚开始的时间ltv,最后由每个事件的最早最晚时间得出活动的最早最晚时间。

结合以下图我们具体来看看关键路径的实现过程,在最开始的时候我们在每个顶点旁边的事件时间表中先初始化最早开始时间都为0,因为后面会再更新。

图中的V1有两个出度分别指向了V2V3V1V2的边权值是2,所以我们就先更新成2。但是不一定是最早的时间,我们先暂时更新一下,等到顶点再更新时再观察是否有更短的时间可以更新。所以V3也就更新成5,然后我们将V1从网中删去得到了下图。

此时的V2就成了拓扑的下一个点,发现V2也有两个出度分别指向了V3V4我们再来看V2V3这条路,很显然2+1 = 3 < 5,说明到V3之前有更耗时的路因为我们需要找在整个工程中更耗时的路线所以我们就不更新V3中的etv接下来看V4中的数据。2+3 = 5 > 0,所以V4中更新为5再将V2从网中删除,结果如下图所示。

就这样依次将路径上的权值与前一个结点的最早开始时间相加与后一个结点的最早开始时间进行对比取最耗时的填入etv中,最后将所有事件的最早开始时间求出来得到了下图。

接下来我们就该求每一个事件最晚的开始时间了,最晚开始时间就是逆拓扑排序,也就是先找出度为0的点,然后删除该结点和头弧,寻找下一个出度为0的结点。同时我们将初始的ltv初始化为12,因为最后的事件若完成那么整个工程就完成了,所以为了不拖延工期,最早的开始时间也是最晚的开始时间。所以如下图所示。

接下来就是逆拓扑的过程,先从V6开始向前面看,可以发现V6有三个入度分别是V3、V4、V5。先看V5ltv的计算就是12-1 = 11 < 12,所以更新V5ltv11,为什么现在取的是较小的值呢?因为,如果这个工程12天完成的话,如果V512天才开始干的话就会变成12+1 = 13 > 12,耽误了一天工期。所以在求最早的时间时选择相加更大的时间更新,在求最晚的时间时选选择更短的时间更新。接下来我们看V4减完后就是8,再看V3减完后就是11,最后的样子如下图所示。

在将 V6 删除后无出度的顶点就成了V5 ,V5的入度有两个分别是V3、V4。接下来仍然是计算部分V411 - 1 = 10 > 8不更新,V3中11 - 4 = 7 < 11进行更新,更新完的结果如下图所示。

剩下的结点同上面一样的步骤最后就能得到事件的最早和最晚开始时间,如下图所示

最后将数据都放在一个表格中。

我们有了事件的时间数据就可以求活动的时间了。

首先先看ete,简单地说就是活动由谁发出就是发出结点的最早开始时间 。a、b由于都是由V1所发出来的所以最早开始时间就是V1的最早开始时间 0,同理c、d都是V2所发出来的,所以c、d的最早开始时间就是V2的最早开始时间2,同理可得全部活动的最早开始时间ete

接下来看lte,简单地说就是活动指向谁,就用被指的结点的最晚时间减去边权值。a指向了V2,所以就是V2的最晚开始时间4减去边权值2,也就是4 - 2 = 2b就是V3的最晚开始时间5减去边权值5也就是5 -5 = 0。同理可得剩下的最晚开始时间lte

此时可以观察到b、e、i三个活动的最早开始时间和最晚开始时间相同,所以b、e、i这三个活动的路径就是我们所求的关键路径。

接下来则是关键路径的代码实现:

这里我们需要用到的拓扑排序和之前的拓扑排序有些许的出入,下面的代码用注释将新添加的部分标注了出来,与之前拓扑排序相同的结构这里就不再给出。

int* etv;//事件最早发生时间数组 int* ltv;//事件最晚发生时间数组 int* stack2;//用于存储拓扑序列的栈 int top2;//用于stack2的指针 //拓扑排序,若GL无回路,则输出拓扑排序序列并返回1,若有回路则返回0 Status TopologicalSort(GraphAdjList GL) { EdgeNode* e; int i, k, gettop; int top = 0;//用于栈指针下标 int count = 0;//用于统计输出顶点的个数 int* stack;//建栈将入度为0的顶点入栈 stack = (int*)malloc(GL->NumVertex * sizeof(int)); for (i = 0; i < GL->NumVertex; i++) { if (0 == GL->adjlist[i].in)//将入度为0的顶点入栈 { stack[++top] = i; } } //***********************************添加部分↓************************************************ top2 = 0;//初始化 etv = (int*)malloc(GL->NumVertex * sizeof(int));//事件最早发生实践数组 for (i = 0; i < GL->NumVertex; i++) { etv[i] = 0;//初始化 } stack2 = (int*)malloc(GL->NumVertex * sizeof(int));//初始化拓扑序列栈 //***********************************添加部分↑************************************************ while (top != 0) { gettop = stack[top--];//出栈 printf("%d -> ", GL->adjlist[gettop].data);//打印此顶点 count++;//统计输出顶点数 //************************************添加部分↓********************************************** stack2[++top2] = gettop;//将弹出的顶点序号压入拓扑序列的栈 //************************************添加部分↑********************************************** for (e = GL->adjlist[gettop].firstedge; e; e = e->next);//对此顶点弧表遍历 { k = e->adjvex; if (!(--GL->adjlist[k].in))//将k号顶点邻接点的入度减1,若为0则入栈,以便下次循环输出 { stack[++top] = k; } //**************************************添加部分↓*********************************************** if ((etv[gettop] + e->weight) > etv[k])//求各顶点事件的最早发生时间etv的值 { etv[k] = etv[gettop] + e->weight; } //**************************************添加部分↑*********************************************** } } if (count < GL->NumVertex)//count小于顶点数,说明存在环 { return ERROR; } else return OK; }

新的拓扑序列就只是多了处理事件最早发生时间 etv 和存储拓扑排序用于在主函数中进行逆拓扑的功能,整体的改动并不多,原理和拓扑是一样的这里不再过多的进行解释。

接下来则是关键路径的主体函数

//求关键路径,GL为有向图 void CriticalPath(GraphAdjList GL) { EdgeNode* e; int i, gettop, k, j; int ete, lte;//活动最早发生时间和最晚发生时间 TopologicalSort(GL);//求拓扑序列,计算数组etv和stack2的值 ltv = (int*)malloc(GL->NumVertex * sizeof(int));//事件发生最晚时间数组 for (i = 0; i < GL->NumVertex; i++) { ltv[i] = etv[GL->NumVertex - 1];//初始化 } while (top2 != 0) { //计算ltv gettop = stack2[top2--]; for (e = GL->adjlist[gettop].firstedge; e; e = e->next) { k = e->adjvex; if (ltv[k] - e->weight < ltv[gettop]) { //求各顶点事件最晚发生时间 ltv[gettop] = ltv[k] - e->weight; } } } for (j = 0; j < GL->NumVertex; j++)//求ete,lte和关键活动 { for (e = GL->adjlist[j].firstedge; e; e = e->next) { k = e->adjvex; ete = etv[j];//活动最早发生时间 lte = ltv[k] - e->weight;//活动最晚发生时间 if (ete == lte)//两者相等即在关键路径上 { printf("<v%d - v%d> length: %d \n", GL->adjlist[j].data, GL->adjlist[k].data, e->weight); } } } }

前面的内容仍旧是定义变量然后求得拓扑之后的序列和etv,值得注意的是这里对ltv进行初始化第一个结点是以V0开始的 ,如果以V1开始的话初始化的时候就不需要再进行-1操作了。然后再往下的while循环中就是对拓扑排序的逆过程和求最早最晚的活动,我们从stack2中取出度为0的结点,然后在for循环中进行计算ltv得出所有的最晚事件的时间后进入后面的for循环中求etelte再进行比较得出关键路径。到此有关关键路径的部分就到此结束了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/20 20:33:45

蓝桥杯JAVA--启蒙之路(三)语句

一前言今天依旧更新有关JAVA基础的知识&#xff0c;唉。自从更新JAVA之后浏览量什么的都下降了&#xff0c;可能是大家也不喜欢这么枯燥的基础学习吧&#xff0c;但是基础还是很重要的&#xff0c;明天和后天可能会停更&#xff0c;因为我要回家了。二主要内容if条件判断&#…

作者头像 李华
网站建设 2026/1/16 20:29:01

金融级情绪识别模型训练全攻略(基于千万级对话数据的优化经验)

第一章&#xff1a;金融客服Agent情绪识别的技术背景与业务价值 在金融服务领域&#xff0c;客户与客服代理&#xff08;Agent&#xff09;之间的交互质量直接影响用户满意度与品牌信任度。随着人工智能技术的发展&#xff0c;尤其是自然语言处理与语音情感分析的进步&#xff…

作者头像 李华
网站建设 2026/1/18 3:20:28

计算机系统基础 bufbomb 实验三

听报告无事&#xff0c;顺手写下做过的实验报告,话不多说&#xff0c;开始正文1、实验目的加深对IA-32函数调用规则和栈帧结构的理解。2、实验原理对目标程序实施缓冲区溢出攻击&#xff0c;通过造成缓冲区溢出来破坏目标程序的栈帧结构&#xff0c;继而执行一些原来程序中没有…

作者头像 李华
网站建设 2026/1/21 14:13:14

Tomcat内存机制以及按场景调优

Tomcat内存机制深度解析与场景化调优 Tomcat作为Java生态中最主流的Web容器&#xff0c;其内存管理直接决定应用的稳定性、响应速度和并发能力。本文将从内存机制底层原理、内存区域划分、常见问题根源&#xff0c;到不同业务场景的调优策略&#xff0c;进行超详细、全维度的拆…

作者头像 李华
网站建设 2026/1/20 20:14:21

ConvertX:自托管的在线文件转换器

ConvertX&#xff1a;自托管的在线文件转换器 在当今信息化时代&#xff0c;文件格式的多样性带来了很多不便。无论是处理文档、图像、视频还是音频&#xff0c;往往需要将文件转换成适合自己需求的格式。为了解决这一问题&#xff0c;ConvertX应运而生&#xff0c;它是一款强大…

作者头像 李华
网站建设 2026/1/19 3:16:15

2025年支持企业实现社会价值与商业价值的战略

在2025年&#xff0c;企业面临的挑战是同时实现社会价值与商业价值。通过创新战略&#xff0c;企业可以有效应对这一挑战。首先&#xff0c;构建以社会责任为核心的商业模式&#xff0c;将信任与责任感融入品牌之中&#xff0c;能够带来更高的顾客忠诚度和市场竞争力。其次&…

作者头像 李华