版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
7.1图的基本概念
7.2图的存储结构
7.3图的遍历
7.4生成树和最小生成树
7.5最短路径
7.6拓扑排序
7.7关键路径7.1图的基本概念1.图的定义对于图(Graph)这一类较复杂的非线性数据结构,可以用两个集合V和E来表示其组成,在形式上可记为G = (V,E)其中V是顶点(Vertex)的非空有限集合;而E是边(Edge)的有限集合,用于表示V中任意两个顶点之间的关系集合。2.图的相关术语1)无向图在一个图中,如果任意两个顶点构成的偶对E是无序的,即顶点之间的连线是无方向的,则该图称为无向图。图7.2(a)所示为一个无向图G1,记为G1 = (V1,E1)其中在一个无向图中,若任意两顶点之间都存在一条直接连线,则称该图为完全无向图。n个顶点的完全无向图有n(n-1)/2条边。2)有向图在一个图中,如果任意两个顶点构成的偶对
E是有序的,即顶点之间的连线是有方向的,则该图称为有向图。图7.1(a)所示为一个有向图G2,记为G2 = (V2,E2)其中在一个有向图中,若任意两顶点之间都存在方向互为相反的两条直接连线,则称该图为完全有向图。n个顶点的完全有向图有n(n-1)条边。3)顶点、边和弧图结构中的数据元素
称为顶点
。在无向图中,
表示在顶点
和顶点
之间有一条直接连线,称这条连线为边。在有向图中,
表示在顶点
和顶点
之间有一条直接连线,称这条连线为有向边(或弧),顶点称
为弧的起点(弧尾),顶点
称为弧的终点(弧头)。4)邻接点在无向图中,若存在边
,则称顶点
和
互为邻接点,即相互邻接,称边
依附于顶点
和
或称边
与顶点
和
相关联。在有向图中,若存在边
,则称顶点
邻接到
或
邻接自
,称边
依附于顶点
和
,或称边
与顶点
和
相关联。图7.1(b)存在有向边
,我们可以说顶点
邻接到
,或者顶点
邻接自
;也可以说边
依附于顶点
和
,或者边
与顶点
和
相关联。5)顶点的度、入度和出度在无向图中,与某一顶点
相关联的边的数目称为
的度,记为
。在有向图中,顶点
的度定义为该顶点的入度和出度之和,其中
的入度定义为以顶点
为终点的边的数目,记为ID(vi);
的出度定义为以顶点
为起点的边的数目,记为
。图7.1(a)无向图G1的顶点
的
,而图7.1(b)有向图G2的顶点
的
,其中
,
。对于具有n个顶点、e条边或弧的图,假设每个顶点的度为
(1≤i≤n),则6)边的权和网与边有关的数据信息称为权。在实际应用中,权值可以有某种特殊含义。边上带权的图称为网络,简称网。7)路径和路径长度顶点vp到顶点vq之间的路径是指顶点序列vp,vi1,vi2,…,vim,vq,其中(vp,vi1),(vi2,vi3),…,(vim,vq)分别为图中的边。路径上边的数目称为路径长度。在图7.1(b)有向图G2中,v1、v2、v4、v5是从顶点v1到顶点v5的一条路径,其路径长度为3。8)简单路径、回路和简单回路顶点序列中顶点不重复出现的路径称为简单路径。路径中第一个顶点与最后一个顶点相同的路径称为回路或者环。除第一个和最后一个顶点外,其他顶点不重复出现的回路称为简单回路或者简单环。在图7.2(b)有向图G2中,v1、v3、v5就是一条简单路径;v1、v2、v4、v5、v1是回路,也是简单回路;v3、v5、v1、v2、v4、v5、v1、v3是回路,但不是简单回路。9)子图对于图G = (V,E)和G'= (V',E'),若
,
,则图G’是G的一个子图。图7.1中G1和G2的其中一个子图分别为
和
,如图7.2所示。10)连通、连通图和连通分量在无向图中,若从一个顶点
到另一个顶点
存在路径,则称顶点
和顶点
是连通的。若图中任意两个顶点都是连通的,则称该图为连通图。无向图的极大连通子图称为连通分量。图7.3(a)中G3为非连通图,它的两个连通分量如图7.3(b)所示。11)强连通图和强连通分量对于有向图来说,若图中任意一对顶点
和顶点
均存在从
到
和从
到
的路径,则称该图为强连通图。有向图的极大强连通分量子图称为强连通分量。图7.4(a)中G4为非强连通图,它的两个强连通分量如图7.4(b)所示。12)稀疏图和稠密图对于具有n个顶点、e条边的图,若
,则称该图为稀疏图,否则为稠密图。13)生成树连通图的生成树是包含其全部顶点的一个极小连通子图。这里的极小连通子图是指在包含所有顶点且保证连通的前提下尽可能少地包含原图中的边。在生成树中添加任意一条属于原图的边必定产生回路,减少任意一条边必定成为非连通。所以一棵具有n个顶点的生成树有且仅有n-1条边。14)生成森林在非连通图中,由于每个连通分量都可以得到一个极小连通子图,即一棵生成树,这些连通分量的生成树就组成了一个非连通图的生成森林。本章讨论的内容不考虑下列图的两种特殊情形:(a)存在顶点到其自身的边或弧,如图7.5所示;(b)两个顶点之间重复出现的边或弧,如图7.6所示。7.2图的存储结构7.2.1邻接矩阵在邻接矩阵(AdjacencyMatrix)存储方法中,图中各个顶点的数据信息存放在一个一维数组中,而图中各顶点之间的邻接关系(边的信息)用一个二维数组(又称为邻接矩阵)来表示。n个顶点的图的邻接矩阵A[n][n]的元素值定义为对于无向图的邻接矩阵,第i行或第i列中的非零元素个数等于对应顶点的度。由于无向图的边的特点,可知无向图的邻接矩阵是对称的,在存储时可以只保存矩阵的下三角或上三角的元素。在有向图中,第i行非零元素的个数对应于该顶点的出度,而第i列非零元素的个数则对应于该顶点的入度。图7.1(a)中G1的邻接矩阵为图7.1(b)中G2的邻接矩阵为易知无向图G1的邻接矩阵A1为对称矩阵,而有向图G2的邻接矩阵A2为非对称矩阵。很容易把图的邻接矩阵推广到网络。网络的邻接矩阵A[n][n]元素值定义为图7.7所示是有向网络G5及其邻接矩阵。n个顶点的图G的存储结构定义为算法7.1无向图的邻接矩阵建立算法。7.2.2邻接表邻接表(AdjacencyList)是一种结合顺序存储与链式存储的存储方法。其中顺序存储部分称为顶点表,用于保存顶点信息;链式结构即邻接链表,用于存储图中边的信息。当图中的顶点很多而边很少时,可以用链式结构取代稀疏的邻接矩阵,从而节省存储空间。顶点表是一个具有两个域的结构体数组,其中一个域称为顶点域(vertex),用来存放顶点本身的数据信息;而另一个域称为指针域(link),用于指示依附于该顶点的边所组成的单链表的第一个结点。顶点vi的邻接链表是由vi所有邻接点链接成的单链表。邻接链表中的每个结点由邻接点域和链域构成。邻接点域(adjvex)用于存放vi的邻接点在顶点表中所处单元的下标;链域(next)用于指向邻接链表中的下一结点。下面给出邻接链表和顶点表中的结点的数据类型:无向图的邻接链表又称为边表。这是因为边表中每个结点都对应与顶点vi相关联的一条边,而边表的长度就是顶点vi的度。图7.8所示为图7.1中无向图G1的邻接表(边表),其中顶点v3的度3就是其关联的邻接链表的长度。在有向图中,vi的邻接链表中结点的个数则对应于vi的出度,因此有向图的邻接链表也称为出边表。同样,也可以建立一个有向图的逆邻接链表,即入边表。vi的入边表中每个结点对应于以vi为终点的一条边。图7.9(a)和图7.9(b)所示分别为图7.1中有向图G2的邻接表和逆邻接表,其中顶点v5的出度1就是其关联的邻接链表的长度,入度2就是其关联的逆邻接链表的长度。算法7.2无向网络的邻接表建立算法。对于有向图邻接表的建立,只需去除算法7.2中生成邻接点序号为i的边表结点 s,并将 s插入顶点vj边表头部的那一段语句组即可。如果要建立网络的邻接表,则需在边表每个结点中增加一个数据域以存储边上的权值。算法7.2的时间复杂度是O(n+e)。邻接矩阵和邻接表都是最常用的图的存储结构。在具体应用中如何选取存储方法,应综合考虑算法本身的特点和空间的存储密度。下面具体分析和比较它们的优缺点:(1)由于邻接链表中结点的链接次序取决于邻接表的建立算法和边的输入次序,因此图的邻接表表示是不唯一的;而图的邻接矩阵表示是唯一的。(2)要判定(vi,vj)或〈vi,vj〉是否是图中的一条边,在邻接矩阵表示中只需随机读取矩阵单元(i,j)的元素值是否为零即可;而在邻接表中,需要扫描vi对应的邻接链表,在最坏情况下需要的扫描时间为O(n)。(3)计算图中边的数目时,对于邻接矩阵必须对整个矩阵进行扫描后才能确定,其时间耗费为O(n2),与e的大小无关;而在邻接表中只需统计每个边表中的结点个数便可确定,时间耗费仅为O(n + e)。当e<<n2时,节省的计算时间非常可观。7.3图的遍历7.3.1深度优先搜索遍历1.深度优先搜索遍历概述图的深度优先搜索(Depth-FirstSearch,DFS)遍历类似于树的先序遍历,是树的先序遍历的推广。假设初始状态是图中所有顶点都未被访问,深度优先搜索方法的步骤是:(1)首先选取图中某一顶点vi为出发点,访问并标记该顶点。(2)以vi为当前顶点,依次搜索vi的每个邻接点vj。若vj已被访问过,则搜索vi的下一个邻接点,否则访问和标记邻接点vj。(3)以vj为当前顶点,重复步骤(2),直到图中和vi有路径相通的顶点都被访问为止。(4)若图中尚有顶点未被访问过(非连通的情况下),则可任取图中的一个未被访问的顶点作为出发点,重复上述过程,直至图中所有顶点都被访问。与树的深度优先搜索类似,这种方法在访问了顶点vi以及vi的一个邻接点vj之后,马上又访问vj的一个邻接点,依此类推,搜索朝着纵深方向进行,所以称为深度优先搜索遍历。图7.10给出了从顶点A出发的深度优先搜索遍历的示例,其中虚线表示搜索路线。显然这种搜索方法具有递归的性质。2.DFSA的算法及步骤选择邻接矩阵作为图的存储结构,则图的深度优先搜索遍历如算法7.3所示。算法7.3图的深度优先搜索遍历DFSA。下面根据算法7.3详细讨论图7.10的深度优先搜索遍历过程。调用函数DFSA时,以相关顶点在顶点表的数组下标作为实参,选取顶点A为遍历的初始出发点,其在顶点表的数组下标为0。(1)调用DFSA(0)来访问顶点A,将visited[0]置为1,表示顶点A已被访问过(简称标记顶点A,下同)。按顺序首先选择A未被访问的邻接点B,进行DFS遍历。(2)调用DFSA(1)访问顶点B,标记顶点B。按顺序选择B的下一个未被访问的邻接点D,进行DFS遍历。(3)调用DFSA(3)来访问顶点D,标记顶点D。按顺序选择顶点D的下一个未被访问的邻接点E,接着从E出发进行DFS遍历。(4)调用DFSA(4)来访问顶点E,标记顶点E。由于顶点E的所有邻接点均已被标记为访问过,结束DFSA(4)的调用并退回到上一层调用DFSA(3)中。此时顶点D的所有邻接点均已被标记过,结束DFSA(3)的调用并继续退回到上一层调用DFSA(1)。基于同样理由继续退回DFSA(0),按顺序选择顶点A的下一个未被访问的邻接点C,因此以顶点C为出发点进行DFS遍历。(5)调用DFSA(2)来访问顶点C,标记顶点C。 此时顶点C的所有邻接点均已被访问过,退回到上一层调用DFSA(0),顶点A的所有邻接点均已被标记过。此时与出发点A有路径相通的所有顶点已被访问过,该连通图的遍历过程结束,所得到的DFS序列为A、B、D、E、C。在算法7.3中,每个顶点vi仅被标记一次,因此至多调用函数DFSA(i)一次。每次调用中for循环要运行n次,所以算法的时间复杂度为O(n2)。由于是递归调用,需要使用长度为n的辅助数组以及一个长度为n–1的工作栈,所以算法的空间复杂度为O(n)。3.DFSL算法以邻接表为存储结构的深度优先搜索遍历如算法7.4所示。可见,只需将算法7.3作适当修改即可。同样,由于图的邻接表表示不唯一,以邻接表为存储结构的DFS序列也不唯一。DFSL算法需要对n个顶点的所有边表结点扫描一遍,而边表结点的数目为2e,所以算法的时间复杂度为O(n+2e),空间复杂度为O(n)。算法7.4图的深度优先搜索遍历DFSL。7.3.2广度优先搜索遍历1.广度优先搜索概述图的广度优先搜索(Breadth-firstSearch,BFS)遍历类似于树的按层次遍历。假设初始状态是图中所有顶点都未被访问,广度优先搜索的思路是:从图中某一顶点vi出发,先访问vi,然后访问vi的所有邻接点vj;当所有的vj都被访问之后,再访问vj的邻接点vk;依此类推,直到图中所有已被访问的顶点的邻接点都被访问过;此时,与初始出发点vi有路径相通的顶点都将被访问。若图是非连通的,则另需选择一个未曾被访问的顶点作为出发点,重复以上过程,直到图中所有顶点都被访问为止。这种遍历方法的特点是,“先被访问的顶点的邻接点”也先于“后被访问的顶点的邻接点”访问,因此具有先进先出的特性。所以需要使用队列来保存已访问过的顶点,以确定对访问过的顶点的邻接点的访问次序。这里仍需使用一个辅助数组visited[n]来标记顶点的访问情况,以避免对某一个顶点的重复访问。2.BFSA和BFSL算法算法7.5图的广度优先搜索遍历BFSA。算法7.6图的广度优先搜索遍历BFSL。3.BFSA算法的性能分析图7.11给出了以邻接矩阵为存储结构时,图的广度优先搜索遍历过程。按图的广度优先搜索遍历顺序得到的顶点序列称为该图的广度优先搜索遍历序列,简称为BFS序列。一个图的BFS序列也不是唯一的,它取决于算法、图的存储结构和初始出发点。以邻接表作为存储结构得到的BFS序列还与邻接表中边表结点的链接次序有关。下面仅以邻接矩阵为例,讨论图7.11的广度优先搜索遍历过程。调用函数BFSA时,以相关顶点在顶点表的数组下标作为实参,以顶点A为遍历的初始出发点,其在顶点表的数组下标为0。(1)调用BFSA(0),顶点A首先被访问;将visited[0]置1并将顶点A的下标值0入队;然后,当队列不为空时执行出队操作得到顶点下标值0,通过搜索顶点A的邻接点可得到未被访问的顶点为B、C和E,所访问的第二、三和四个顶点分别为B、C和E;将visited[1]、visited[2]和visited[4]置1,并将其对应顶点的下标值1、2和4入队。(2)此时出队的顶点下标值是1,搜索到B的三个邻接点A、D、E中有顶点D未被访问,因此D为第五个被访问的顶点;置visited[3]为1,并将其下标值3入队。(3)此时出队的顶点下标值是2,对应顶点C的邻接点A已访问过,故不执行入队操作。(4)接着出队的顶点下标值是4,搜索到E的两个邻接点B和D均已访问过,故不执行入队操作。(5)最后出队的顶点下标值是3,搜索可知D的两个邻接点B和E都已访问过,故无顶点下标值入队。此时队列已为空,所以搜索过程结束,根据访问的顺序得到的BFS遍历序列是A、B、C、E、D。分析可知,对于有n个顶点和e条边的连通图,BFSA算法的while循环和for循环都需执行n次,所以BFSA算法的时间复杂度为O(n2);BFSL算法的while循环要执行n次,而for循环执行次数等于边表结点的总个数2e,因此BFSL算法的时间复杂度为O(n+2e)。BFSA算法和BFSL算法都使用了一个长度均为n的队列和辅助标志数组,因此空间复杂度都为O(n)。7.4生成树和最小生成树7.4.1生成树和生成森林以无向连通图G为例,把遍历过程中顺序访问的两个顶点之间的路径记录下来。这样G中的n个顶点以及由出发点依次访问其余n-1个顶点所经过的n-1条边就构成了G的极小连通子图,也就是G的一棵生成树,而出发顶点是生成树的根。所谓极小是指该子图具有连通所需的最小边数,若去掉一条边,该子图就变成了非连通图;若任意增加一条边,该子图就有回路产生。通常我们将深度优先搜索得到的生成树称为深度优先搜索生成树,简称为DFS生成树;而将广度优先搜索得到的生成树称为广度优先搜索生成树,简称为BFS生成树。图7.12给出了对图7.10和图7.11从顶点A出发进行遍历所产生的DFS生成树和BFS生成树。在图论中,树可以看作是一个无回路存在的连通图,连通图G的生成树就可以定义为一个包含了G的所有顶点的树。由树的性质也可知,一个连通图G具有n个顶点,其生成树最多有n-1条边。该生成树也是G的一个极小连通子图。显然,连通图的生成树并不唯一,这取决于选择的遍历方法和出发顶点。对于非连通图,遍历其每个连通分量则可生成对应的生成树,这些生成树构成了非连通图的生成森林。7.4.2最小生成树下面给出最小生成树(MinimumSpanningTree,MST)的概念。给定一个连通网络,要求构造具有最小代价的生成树,即生成树各边的权值总和达到最小。把生成树各边的权值总和定义为生成树的权,那么具有最小权值的生成树就构成了连通网络的最小生成树。最小生成树的构造具有重要的实际应用价值。例如要在n个城市之间建立通信光纤网络,而不同城市之间铺设光纤的造价不同,要想使总造价最低,实际上就是寻找该网络的最小生成树,也就是可将这一问题转化为构造最小生成树的问题。构造最小生成树,也就是在给定n个顶点所对应的权矩阵(代价矩阵)的条件下,给出代价最小的生成树。构造最小生成树的算法有多种,其中大多数算法都利用了最小生成树的一个性质,简称MST性质。假设G = (V,E)是一个无向连通网络,U是V中的一个非空子集,若存在顶点uU和顶点vV-U的边(u,v)是一条具有最小权值的边,则必存在G的一棵最小生成树包括这条边(u,v)。MST性质可用反证法加以证明。假设网络G中的任何一棵最小生成树T都不包含(u,v),其中uU,vV-U。在最小生成树T中,必然存在一条边(u′,v′)连接两个顶点集U和V-U,其中u′U,v′V-U。当(u,v)加入到T中时,T中必然存在一条包含了(u,v)的回路。如果删除(u′,v′)并保留(u,v),则得到另一棵生成树T′。因为(u,v)的权小于(u′,v′)的权,故T′的权小于T的权,这与假设相矛盾。下面介绍的Prim(普里姆)算法和Kruskal(克鲁斯卡尔)算法是利用MTS性质构造最小生成树的两种经典算法。7.4.3构造最小生成树的Prim算法1.Prim算法概述假设G = (V,E)是有n个顶点的连通网络,用T = (U,TE)表示要构造的最小生成树,其中U为顶点集合,TE为边的集合。以下是Prim算法的具体步骤。(1)初始化。令U = φ,TE =φ,从V中取出一个顶点u0放入生成树的顶点集U中作为第一个顶点,此时T=({u0},φ)。(2)从所有满足uU、vV-U的边(u,v)中找一条代价最小的边(u*,v*),将其放入TE中,并将v*放入U中。(3)重复步骤(2),直至U=V为止。此时集合TE中必有n–1条边,T即为所要构造的最小生成树。Prim算法的关键是第(2)步,即如何找到连接U和V-U的最短边(代价最小边)。假设生成树T中已有k个顶点,则U和V-U中可能存在的边数最多为k(n-k)条,从如此之大的边集合中选取最短边是十分不可行的。可以这样来构造候选最短边的集合:对于V-U中的每个顶点,只保留从该顶点到U中某顶点的最短边,即候选最短边的集合为V-U中n-k个顶点所关联的n-k条最短边的集合。只要有新的顶点加入U(此时V-U集合中元素也发生变化),就需要更新候选最短边的集合。2.Prim算法的步骤Prim算法的基本思想用伪代码描述如下:为具体实现Prim算法,设立以下边的存储结构:3.Prim算法的代码算法7.7最小生成树的Prim算法。算法7.7中构造第一个顶点所需的时间是O(n),求k条边的时间大约为其中O(1)表示某一正常数C,所以式(7-3)的时间复杂度是O(n2)。可见Prim算法的复杂度与网的边数无关,适合于边稠密网络的最小生成树。7.4.4构造最小生成树的Kruskal算法Kruskal算法是从另一途径来求网络的最小生成树。设G=(V,E)是一个有n个顶点的连通图,则令最小生成树的初始状态为只有n个顶点而无任何边的非连通图T=(V,),图中每个顶点自成一个连通分量。依次选择E中的最小代价边,若该边依附于T中两个不同的连通分量,则将此边加入TE中;否则,舍去此边而选择下一条代价最小的边。依此类推,直到T中所有顶点都在同一连通分量上为止。这时的T就是G的一棵最小生成树。利用Kruskal算法对图7.13(a)所示的网络构造最小生成树,过程如图7.14所示。Kruskal算法的基本思想用伪代码描述如下:在Kruskal算法中,每次选择最小代价边都需要扫描图所有边中的最短边。若用邻接矩阵实现,则需要扫描整个邻接矩阵,算法复杂度太高;而使用邻接表时,由于每条边都被连接两次,因此寻找最短边的计算时间加倍。所以我们对图中的边采用如下的存储结构:这里数组G存放顶点的连通信息,用于判断所选择的边对应的两个顶点是否在同一个分量上。具体描述如算法7.8所示。算法7.8最小生成树的Kruskal算法。7.5最短路径7.5.1从某个源点到其他各顶点的最短路径从某个源点到其他各顶点的最短路径问题称为“单源最短路径问题”。单源最短路径问题可以描述为:对于给定的有向网络G = (V,E)及源点,求从u0到G中其他各顶点的最短路径。假设各边上的权值大于或等于0,单源最短路径问题可以用迪杰斯特拉(EdsgerWybeDijkstra)提出的最短路径算法来解决。迪杰斯特拉最短路径算法的思路源自迪杰斯特拉对大量单源最短路径顶点构成的集合和路径长度之间关系的研究。若按长度递增的次序来产生源点到其余顶点的最短路径,则当前正要生成的最短路径除终点外,其余顶点的最短路径已生成。换句话说,假设A为源点,U为已求得的最短路径的终点的集合 (初态时为空集),则下一条最短路径 (设其终点为X) 或者是弧(A,X),或者是中间只经过U集合中的顶点,最后到达X的路径。这条最短路径上不可能存在U集合之外的顶点。这一点,读者可以用反证法加以证明。为验证以上观点,图7.15所示为有向网络G以及以顶点A为源点的最短路径集合。可以看出,在查找终点为E、B的最短路径时,其中间顶点均是已求得的最短路径终点的集合。根据以上思路,可以按路径长度递增次序来产生源点到各顶点的最短路径。对有向连通网络G=(V,E),假设顶点个数为n,以u0作为源点,算法步骤描述如下:(1)从V中取出源点u0放入最短路径顶点集合U中,此时最短路径网络S=({u0},)。(2)从uU和vV-U中找到一条最小代价边(u*,v*),将其加入到S中去,更新S =({u0,v*},{(u0,v*)})。(3)每往U中增加一个顶点,就要对V-U中各顶点的权值进行一次修正:若加进v*作为中间顶点,使得从u0到其他属于V-U的顶点vi的路径不加v* 时最短,则修改u0到vi的权值,即以(u0,v*)的权值加上(v*,vi)的权值来代替原(u0,vi)的权值,否则不修改u0到vi的权值。(4)从权值修正后的V-U中选择最短的边加入S中,如此反复,直到U=V为止。Dijkstra算法的基本思想用伪代码描述为:算法7.9所示为C语言描述的迪杰斯特拉算法。其中有向网络用邻接矩阵dist[n][n]表示;数组D[n]用于保存源点到其他顶点的最短距离;数组p[i]用于记录最短路径中顶点i的前驱顶点;数组s[n]则用于标识最短路径的生成情况,s[i]=1表示源点到顶点i的最短路径已产生,s[i]=0表示最短路径还未产生。算法7.9迪杰斯特拉(Dijkstra)最短路径算法。对图7.14中的有向网络G执行Dijkstra算法,以A点为源点时的数组D[n]、p[n]和s[n]的更新状况如表7.2所示。相应的打印输出结果为:7.5.2每一对顶点之间的最短路径人们可能只希望找到源点到某个顶点的最短路径,但是其这个问题的时间复杂度也是O(n2)。为求解这一问题,可以把有向网络的每个顶点依次作为源点并执行Dijkstra算法,从而求得每一对顶点之间的最短路径。这种方法中Dijkstra算法需要重复执行n次,因此总的时间复杂度为O(n3)。弗洛伊德(RobertW.Floyd)于1962年提出了解决这一问题的另一种算法。该算法的时间复杂度也是O(n3),但是形式比较简单,易于理解。本节主要介绍这种算法。Floyd算法是根据有向网络的邻接矩阵dist[n][n]来求顶点vi到顶点vj的最短路径,其基本思路是:假设vi和vj之间存在一条路径,但这并不一定是最短路径,尚需进行n次试探。首先尝试在vi和vj之间增加一个中间顶点v0,若增加v0后的路径(vi,v0,vj)比(vi,vj)短,则以新的路径代替原路径,并且修改dist[i][j]的值为新路径的权值;若增加v0后的路径比(vi,vj)更长,则维持dist[i][j]不变。然后在修改后的dist矩阵中,另选一个顶点v1作为中间顶点,重复以上的操作,直到除vi和vj外的其余顶点都做过中间顶点为止。邻接矩阵dist[n][n]的更新是Floyd算法的重点。依次以顶点v1,…,vn为中间顶点实施以上操作时,将递推地产生出一个矩阵序列D(k)[n][n](k = 0,1,2,…,n)。其中D(0)[n][n]即初始邻接矩阵dist[n][n],表示每一对顶点之间的直接路径的权值;D(k)[n][n](1≤k<n)则表示中间顶点的序号不大于k的最短路径长度。显然,D(n)[n][n]就是每一对顶点之间的最短路径长度。为了记录每一对顶点之间最短路径所经过的具体路径,需另设一个path矩阵,其每个元素path[i][j]所保存的值是从顶点vi到顶点vj的最短路径上vj的前驱顶点序号。类似地,path(0)给出了每一对顶点之间的直接路径,而path(n)给出了每一对顶点之间的最短路径。算法7.10所示为C语言描述的Floyd算法。算法7.10弗洛伊德(Floyd)最短路径算法。7.6拓扑排序我们用顶点表示某个活动,用顶点之间的有向边表示活动之间的先后关系,这样无论是工程、生产还是专业的课程学习,都可以用一个有向图来表示,并称其为顶点表示活动网络(ActivityOnVertexnetwork,简称AOV网)。AOV网中的顶点也可带有权值,表示完成一项活动所需要的时间;AOV网中的有向边表示活动之间的制约关系。这些活动可以表示一个工程中的子工程,一个产品生产中的部件生产,课程学习中的一门课程,因此一般要按一定次序进行。当限制各个活动只能串行进行时,如果可以将AOV网中的所有顶点排列成一个线性序列vi1,vi2,…,vin,并且这个序列同时满足条件:若在AOV网中从顶点vi到顶点vj存在一条路径,则在线性序列中vi必在vj之前,这个线性序列就称为拓扑序列。对AOV网构造拓扑序列的操作称为拓扑排序。AOV网的拓扑序列给出了各个活动按序完成的一种可行方案。AOV网中不应该出现有向环,因为这意味着某些活动是以自己为先决条件,显然是荒谬的。2.拓扑排序的基本操作拓扑排序的基本操作如下:(1)从网中选择一个入度为0的顶点并且输出它。(2)从网中删除此顶点及所有由它发出的边。重复上述两步,直到网中再没有入度为0的顶点为止。以上操作的结果有两种:网中无有向环,则网中的全部顶点被输出到拓扑序列中;网中存在有向环,顶点未被全部输出,剩余顶点的入度均不为0。第二种情况下的拓扑排序是不会成功的。对图7.16的AOV网进行拓扑排序,可以得到一种拓扑序列C1、C2、C7、C9、C4、C6、C8、C3、C5,具体过程如图7.17所示。拓扑排序算法用伪代码描述如下:3.拓扑排序的程序可以采用邻接矩阵表示有向图,但是此时拓扑排序的效率并不高,下面只讨论用邻接表存储有向图的拓扑排序算法。为了便于考察每个顶点的入度,可以在排序算法中增加一个局部向量indegree[n]保存各顶点当前的入度;同时为避免每次选择入度为0的顶点时扫描整个indegree向量,可设置一个栈S暂存当前所有入度为0的顶点。因此,在进行拓扑排序之前,先扫描indegree向量,并将入度为0的顶点下标(顶点在顶点表中对应的数组下标)均入栈。在拓扑排序过程中,每次选入度为0的顶点时,只需要做出栈操作即可。算法中删除入度为0的顶点及其所有出边的操作只需检查出栈的顶点i的出边表,把每条弧〈i,j〉的终点j所对应的入度indegree[j]减1(相当于删除此出边);若indegree[j]变为0,则将j入栈。拓扑排序的算法如算法7.11所示。算法7.11拓扑排序算法(邻接表)。7.7关键路径7.7.1AOE网概述AOE网络中的边表示活动网络,其中每条弧表示一个活动(或称为子工程),弧上的权值表示活动持续的时间;顶点表示事件(Event),用以说明入边所表示的活动均已完成,而出边所表示的活动可以开始。正常情况下,AOE网络应该不存在回路。AOE网可以用于研究关键路径问题。例如,一个实际的大工程中有许多较小的子工程,我们不仅要得到子工程执行的顺序,而且也要估算整项工程的完成时间,以及如何提高整个工程的完成速度。由于一项工程只有一个开始点和一个结束点,因此AOE网络中只有一个入度为0的顶点(称作源点,表示开始)和一个出度为0的顶点(称为汇点,表示结束)。由于AOE网中的某些活动可以并行地进行,因此完成整个工程的最短时间是从源点到汇点的最长路径长度(这里的路径长度是指沿着该路径的各个活动所需持续时间之和)。从源点到汇点的路径长度中最长的称为关键路径(CriticalPath)。图7.18所示为一个AOE网络的示例,其中包括7个事件v1,v2,…,v7,分别表示其之前的活动已经结束而之后的活动可以开始;9个活动,分别用a1,a2,…,a9来表示。AOE网的有向边可以反映活动之间的时间制约关系。有时为了约束某些活动的先后顺序,可增加时间花费为0的有向边,称为虚活动。例如想使活动a4和a5在活动a1之后才开始,可在顶点v2和顶点v3之间增加一个虚线的有向边,表示虚活动〈v2,v3〉。在图7.18中可以很容易地找到两条关键路径,一条是v1、v3、v4、v5、v7,另一条是v1、v3、v4、v6、v7。这两条关键路径的长度都是20。为了求解关键路径问题,需要引入下面几个术语:(1) ve(j):表示事件vj可能的最早发生时间,即为从源点v1到vj的最长路径长度。(2) e(i):表示活动ai的最早开始时间。若活动ai以vj为起点,则(3) v1(k):表示在不推迟整个工程完成的前提下,一个事件vk允许的最迟发生时间,等于汇点vn的最早发生时间ve(n)减去vk到vn的最长路径长度。(4) l(i):表示活动ai的最迟开始时间。若活动ai以vk为终点的入边,则l(i)等于减去ai的持续时间,即(5)关键活动:通常定义为l(i) = e(i)的活动。分析可知,活动ai的最迟开始时间l(i)减去ai的最早开始时间e(i)等于完成活动ai的时间余量,也即在不延误整个工程工期的情况下,活动ai可以延迟的时间。但是如果l(i) = e(i),则说明活动ai完全不能延迟,否则会影响整个工程的进度。关键活动实际上就是关键路径上的各个活动。由于关键路径的长度决定了完成整个工程所需的时间,因此关键活动是缩短整个工期的关键。提前完成关键活动才有可能加快工程进度,而且只有提前完成了包含在所有关键路径上的那些关键活动才能加快工程的进度。7.7.2关键路径的确定方法下面介绍关键活动的辨别方法。假设活动ai由边〈vj,vk〉表示,其持续时间用dur(〈j,k〉)来表示,确定它是否为关键活动就需要判断e(i)是否等于l(i)。为了求得e(i)和l(i),首先要求出ve(j)和vl(j)。ve(j)从源点j=1开始向前递推,递推公式为vl(j)则从汇点j=n开始逆推,递推公式为下面给出图7.18中AOE网络中各个事件的最早发生时间和最迟发生时间:ve(1)=0ve
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 安防监控合同
- 外墙脚手架搭拆分包合同
- 软件系统软件优化与升级服务合同3篇
- 教育电子产品采购合同范本3篇
- 二零二五塔吊司机承包协议(含施工安全防护措施)2篇
- 二零二五年度专业培训机构学员就业跟踪服务合同3篇
- 2025年度墙体图文广告创意设计及后期制作合同3篇
- 二零二五年度文化主题墙绘设计制作外包服务协议2篇
- 2025年度堰塘承包与水资源优化配置协议3篇
- 二零二五年度大数据分析增资扩股及数据服务协议3篇
- 江西省景德镇市2023-2024学年高二上学期1月期末质量检测数学试题 附答案
- 2024年办公楼卫生管理制度模版(3篇)
- 保险公司2024年工作总结(34篇)
- 2024年01月22503学前儿童健康教育活动指导期末试题答案
- 湖北省荆州市八县市2023-2024学年高一上学期1月期末考试 化学 含解析
- 2024年世界职业院校技能大赛中职组“婴幼儿保育组”赛项考试题库-上(单选题)
- 期末测评(基础卷二)-2024-2025学年一年级上册数学人教版
- 深圳大学《数值计算方法》2021-2022学年第一学期期末试卷
- 服装厂安全培训
- 民法债权法学习通超星期末考试答案章节答案2024年
- 2024年9月时政题库(附答案)
评论
0/150
提交评论