数据结构学习笔记——图的应用2(拓扑排序、关键路径)

简介: 数据结构学习笔记——图的应用2(拓扑排序、关键路径)

一、拓扑排序


(一)DAG图和AOV网


对于一个有向图,若图中不存在回路(环),则称该图是一个DAG图;若以DAG图中的顶点表示活动,以边表示活动的先后次序,则称其是一个AOV网。

例如,这就是一个DAG图:

1667297026041.jpg



(二)拓扑排序的概念


对于一个DAG图,其所有顶点组成一个线性序列,且每个顶点只出现一次,对于图G=(V,E)中的任意一对顶点A和B,顶点A在线性序列中排在顶点B的前面,即<A,B>∈E(G),图中存在由A到B的路径,则这种序列称为拓扑序列,称为拓扑排序。

若一个有向无回路图的拓扑序列是唯一的,对其而言并不能唯一确定该图。


(三)拓扑排序的步骤


对于一个AOV网进行拓扑排序:

1、从网中找选择一个没有前驱,即入度ID(v)=0的顶点;

2、删除该顶点且删除由该顶点的所有起始边(出度的边),并将该顶点输出;

3、重复(1)和(2)步骤,直至AOV网为空或不存在没有前驱的结点为止;

4、最后得到一个序列,即拓扑排序。


AOV网的拓扑排序的序列不止一个,也可以能有多个,是由于当网中有多个入度为0的顶点时,进行拓扑排序,得到的序列不止一种。

例如,对于下面这个图,对其进行拓扑排序:

1667297039700.jpg


1、由该图可知,ID(V1)=0、ID(V6)=0,即入度为0的顶点有V1、V6。

2、以V1开始(若以V6序列开始也可以,只是得到的拓扑序列不同),删除顶点V1以及其出度并输出该顶点,得到如下:

1667297048333.jpg

3、由得到的图可知,ID(V3)=0、ID(V6)=0,即入度为0的顶点为V3和V6,以V3继续(若以V6序列也可以,只是得到的拓扑序列不同),删除顶点V3以及其出度并输出该顶点,得到如下:

1667297055414.jpg

4、由得到的图可知,ID(V6)=0,即入度为0的顶点为V6,删除顶点V6以及其出度并输出该顶点,得到如下:

1667297062018.jpg

5、由得到的图可知,ID(V5)=0,即入度为0的顶点为V5,删除顶点V5以及其出度并输出该顶点,得到如下:

1667297072210.jpg

6、由得到的图可知,ID(V4)=0,即入度为0的顶点为V4,删除顶点V4以及其出度并输出该顶点,得到如下:

1667297082660.jpg

7、加上最后的顶点V2,输出该顶点,此时图中所有顶点都输出,即得到该图的一个拓扑序列为V1、V3、V6、V5、V4、V2。


(四)拓扑排序的时间复杂度


若一个有向图的所有顶点不能排成一个拓扑序列(有向图存在回路,则一定不存在拓扑排序),由于该图中一定存在回路,即该图中存在含有顶点数目大于1的强连通分量。

对于图G=(V,E),当采用邻接矩阵存储图时拓扑排序的时间复杂度为O(|V|2);而采用邻接表存储图时,由于在输出顶点时还要删除其所有起始边(出度),所以拓扑排序的时间复杂度为O(|V|+|E|)。


二、逆拓扑排序和DFS算法的应用


(一)逆拓扑排序


若将拓扑排序的步骤中的一开始的针对入度进行排序改为针对出度,则以下是逆拓扑排序的步骤,对于一个AOV网进行逆拓扑排序:

1、从网中找选择一个没有后继,即出度OD(v)=0的顶点;

2、删除该顶点且删除由该顶点的所有终止边(入度的边),并将该顶点输出;

3、重复(1)和(2)步骤,直至AOV网为空或不存在没有后继的结点为止;

4、最后得到一个序列,即逆拓扑排序。


与拓扑排序一样,AOV网的逆拓扑排序的序列不止一个,也可以能有多个,是由于当网中有多个出度为0的顶点时,进行逆拓扑排序,得到的序列不止一种。

例如,对于下面这个图,对其进行拓扑排序:


1667297101657.jpg

1、由该图可知,OD(V3)=0、OD(V5)=0,即出度为0的顶点有V3、V5。

2、以V3开始(若以V5序列开始也可以,只是得到的逆拓扑序列不同),删除顶点V3以及其入度并输出该顶点,得到如下:

1667297109889.jpg

3、由得到的图可知,OD(V5)=0,即出度为0的顶点为V5,以V5继续,删除顶点V5以及其入度并输出该顶点,得到如下:

1667297116290.jpg

4、由得到的图可知,OD(V4)=0,即出度为0的顶点为V4,以V4继续,删除顶点V4以及其入度并输出该顶点,得到如下:

1667297125000.jpg

5、由得到的图可知,OD(V2)=0,即出度为0的顶点为V2,以V2继续,删除顶点V2以及其入度并输出该顶点,得到如下:

1667297132564.jpg

6、加上最后的顶点V1,输出该顶点,此时图中所有顶点都输出,即得到该图的一个逆拓扑序列为V3、V5、V4、V2、V1。


(二)DFS算法的应用


对于一个AOV网也可以采用DFS算法,由于当从有向无环图中某一顶点开始深度优先搜索时,最先得到的顶点即为出度等于0的顶点,所以通过DFS算法得到的顶点序列刚好为逆向的拓扑序列。(DFS算法的知识点可以看之前的文章:图的遍历算法(深度优先搜索和广度优先搜索))


三、关键路径


(一)AOE网


对于AOV网,它是以顶点来表示活动、边上无权值,只表示活动的先后次序;而对于AOE网,是以边来表示活动,且边上有权值,表示完成该活动的开销(时间),其对比如下表:

1667297167301.jpg


(二)源点和汇点


在一个表示工程的AOE网中,只存在一个入度等于0,即ID(v)=0的顶点,称为源点,代表整个工程的开始;同样,也只存在一个出度等于0,即OD(v)=0的顶点,称为汇点,代表整个工程的结束。


(三)关键路径的概念


在AOE网中,由源点到汇点的有向路径可能有多条,从而完成活动的路径长度也不同,将所有路径中具有最大路径长度的路径称为关键路径,且这条路径上的所有活动称为关键活动,是决定整个工程的关键因素,关键路径是整个工程所完成的最短时间。

一个AOE网的关键路径不唯一,若延长关键路径上的关键活动的时间,则整个工程的时间也会随着延长;另外,只有缩短所有的关键路径上共有的任意一个关键活动的持续时间,才会缩短关键路径长度。


(四)关键路径的算法步骤


1、根据所给的图,求得该图的拓扑有序序列和逆拓扑有序序列;

2、通过得到的序列,求得每个事件的最早发生时间和最迟发生时间:

3、根据(2)得到的结果,求得每个活动的最早发生时间和最迟发生时间。

4、根据(3)得到的结果,找到最早发生时间和最迟发生时间相同的活动,就得到了关键活动,由关键活动连成的路径即为关键路径。


例如,下面是一个AOE网,求其关键路径。


1667297189250.jpg

1、首先求其拓扑序列和逆拓扑序列:

拓扑序列和逆拓扑序列分别为V3、V1、V4、V2、V6、V5和V5、V6、V2、V4、V1、V3。

2、设ve(V)为顶点V代表的事件的最早发生时间,依照拓扑序列的先后顺序求各顶点所对应事件的最早发生时间:

设事件0的开始时间为0,即ve(V3)=0,可得:

ve(V1)=ve(V3)+3=0+3=3;

ve(V4)=ve(V3)+3=0+3=3;

由于到V2的路径有两条,分别是V1→V2和V4→V2,要取其中权值最大者,也就是最早发生时间,ve(V2)=max{ve(V1)+2,ve(V4)+6}=max{3+2,3+6}=9;

ve(V6)=ve(V4)+1=3+1=4;

由于到V5的路径有两条,分别是V2→V5和V6→V5,要取其中权值最大者,也就是最早发生时间,ve(V5)=max{ve(V2)+5,ve(V6)+2}=max{9+5,4+2}=14。

3、设vl(V)为顶点V代表的事件的最迟发生时间,最迟发生时间是在不推迟整个工程完成的前提下,该事件最迟必须发生的时间,依照逆拓扑序列的先后顺序求各顶点所对应事件的最迟发生时间,且由事件的最早发生时间,可得:

vl(V5)=ve(V5)=14;

vl(V6)=vl(V5)-2=14-2=12;

vl(V2)=vl(V5)-5=14-5=9;

由于回V4的路径有两条,分别是V3→V1和V3→V4,要取其中权值最小者,也就是最迟发生时间,vl(V4)=min{vl(V2)-6,vl(V6)-1}=min{9-6,12-1}=3;

vl(V1)=vl(V2)-2=9-2=7;

由于回V3的路径有两条,分别是V4→V2和V4→V6,要取其中权值最小者,也就是最迟发生时间,vl(V3)=min{vl(V1)-3,vl(V4)-3}=min{7-3,3-3}=0。

4、求每个活动的最早发生时间和最迟发生时间,设e(ak)和l(ak)为当前活动ak的最早发生时间和最迟发生时间,由于事件代表一个新活动的开始或旧活动的结束,所以事件的最早发生时间就是由这个事件所发出的活动的最早发生时间,即ve(V);而活动的最迟发生时间是事件的最迟发生时间减去以它为结束点的活动的持续时间:

1667297199157.jpg

(1)活动的最早发生时间:

e(a0)=e(a1)=ve(V3)=0;

e(a2)=e(a3)=ve(V4)=3;

e(a4)=ve(V1)=3;

e(a5)=ve(V2)=9;

e(a6)=ve(V6)=4。

(2)活动的最迟发生时间:

l(a0)=vl(V1)-3=7-3=4;

l(a1)=vl(v4)-3=3-3=0;

l(a2)=vl(V2)-6=9-6=3;

l(a3)=vl(V6)-1=12-1=11;

l(a4)=vl(V2)-2=9-2=7;

l(a5)=vl(V5)-5=14-5=9;

l(a6)=vl(V5)-2=14-2=12;

由以上得到的活动的最早/最迟发生时间,整理得关键活动,即最早最早发生时间和最迟发生时间相同的活动,如下:

活动 最早发生时间 最迟发生时间 关键活动
a0 0 4
a1 0 0
a2 3 3
a3 3 11
a4 3 7
a5 9 9
a6 4 12



5、由关键活动所连成的路径即为所求的关键路径,可知关键路径为:

V3→V4→V2→V5,

关键路径所持续的时间为整个工程所持续的时间(整个图中所有活动完成的时间):

a1+a2+a5=0+3+9=12。


例、下图所示的AOE网表示一项包含8个活动的工程,求活动d的最早开始时间和最迟开始时间。

1667297218122.jpg


解:活动d的最早开始时间为事件2的最早发生时间,即max{a,b+c}=max{3,12}=12;求出图的关键路径长度,先求得拓扑排序为1、3、2、4、5、6:

设ve(1)=0,

ve(2)=ve(1)+3=3;

ve(3)=ve(1)+8=8;

ve(4)=ve(2)+7=10;

ve(5)=max{ve(2)+6,ve(3)+10}=max{9,18}=18;

ve(6)=max{ve(4)+6,ve(5)+9}=max{16,27}=27;

所以关键路径长度为27。

由于活动d的最迟开始时间为该活动弧的终点所表示的事件的最迟发生时间与该活动所需时间之差,即min{27-g}=min{21},

所以活动的最迟开始时间为21-d=21-7=14。


相关文章
|
3月前
|
算法 安全 网络安全
数据结构之网络攻击路径(深度优先搜索)
本文介绍了如何使用深度优先搜索(DFS)算法分析网络攻击路径。在网络安全领域,DFS用于检测网络中潜在的攻击路径,帮助安全人员及时发现并阻止威胁。文中详细描述了网络图的构建、节点间的连接关系以及DFS的实现过程。通过一个具体的例子,展示了如何检测从一个普通节点到关键节点的攻击路径,并讨论了DFS算法的优缺点。提供的C++代码实现了网络图的构建和攻击路径的检测功能。
83 24
|
4月前
|
存储 Java
Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。
【10月更文挑战第19天】本文详细介绍了Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。HashMap以其高效的插入、查找和删除操作著称,而TreeMap则擅长于保持元素的自然排序或自定义排序,两者各具优势,适用于不同的开发场景。
63 1
|
4月前
|
存储 算法 C语言
通义灵码在考研C语言和数据结构中的应用实践 1-5
通义灵码在考研C语言和数据结构中的应用实践,体验通义灵码的强大思路。《趣学C语言和数据结构100例》精选了五个经典问题及其解决方案,包括求最大公约数和最小公倍数、统计字符类型、求特殊数列和、计算阶乘和双阶乘、以及求斐波那契数列的前20项和。通过这些实例,帮助读者掌握C语言的基本语法和常用算法,提升编程能力。
126 4
|
14天前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》图、查找、排序专题考点(含解析)
408考研——《数据结构》图,查找和排序专题考点选择题汇总(含解析)。
66 29
|
2天前
|
DataX
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
1月前
|
存储 人工智能 算法
【C++数据结构——内排序】二路归并排序(头歌实践教学平台习题)【合集】
本关任务是实现二路归并算法,即将两个有序数组合并为一个有序数组。主要内容包括: - **任务描述**:实现二路归并算法。 - **相关知识**: - 二路归并算法的基本概念。 - 算法步骤:通过比较两个有序数组的元素,依次将较小的元素放入新数组中。 - 代码示例(以 C++ 为例)。 - 时间复杂度为 O(m+n),空间复杂度为 O(m+n)。 - **测试说明**:平台会对你编写的代码进行测试,提供输入和输出示例。 - **通关代码**:提供了完整的 C++ 实现代码。 - **测试结果**:展示代码运行后的排序结果。 开始你的任务吧,祝你成功!
36 10
|
1月前
|
搜索推荐 算法 数据处理
【C++数据结构——内排序】希尔排序(头歌实践教学平台习题)【合集】
本文介绍了希尔排序算法的实现及相关知识。主要内容包括: - **任务描述**:实现希尔排序算法。 - **相关知识**: - 排序算法基础概念,如稳定性。 - 插入排序的基本思想和步骤。 - 间隔序列(增量序列)的概念及其在希尔排序中的应用。 - 算法的时间复杂度和空间复杂度分析。 - 代码实现技巧,如循环嵌套和索引计算。 - **测试说明**:提供了测试输入和输出示例,帮助验证代码正确性。 - **我的通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了代码运行的测试结果。 通过这些内容,读者可以全面了解希尔排序的原理和实现方法。
58 10
|
1月前
|
搜索推荐 C++
【C++数据结构——内排序】快速排序(头歌实践教学平台习题)【合集】
快速排序是一种高效的排序算法,基于分治策略。它的主要思想是通过选择一个基准元素(pivot),将数组划分成两部分。一部分的元素都小于等于基准元素,另一部分的元素都大于等于基准元素。然后对这两部分分别进行排序,最终使整个数组有序。(第一行是元素个数,第二行是待排序的原始关键字数据。本关任务:实现快速排序算法。开始你的任务吧,祝你成功!
41 7
|
4月前
|
机器学习/深度学习 存储 人工智能
数据结构在实际开发中的广泛应用
【10月更文挑战第20天】数据结构是软件开发的基础,它们贯穿于各种应用场景中,为解决实际问题提供了有力的支持。不同的数据结构具有不同的特点和优势,开发者需要根据具体需求选择合适的数据结构,以实现高效、可靠的程序设计。
270 63
|
3月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
99 5