数据结构之最短路径
- 1、单源点最短路径
- 2、每对顶点间的最短路径
数据结构是程序设计的重要基础,它所讨论的内容和技术对从事软件项目的开发有重要作用。学习数据结构要达到的目标是学会从问题出发,分析和研究计算机加工的数据的特性,以便为应用所涉及的数据选择适当的逻辑结构、存储结构及其相应的操作方法,为提高利用计算机解决问题的效率服务。
数据结构是指数据元素的集合及元素间的相互关系和构造方法。元素之间的相互关系是数据的 逻辑结构,数据元素及元素之间关系的存储称为 存储结构(或物理结构)。数据结构按照逻辑关系的不同分为 线性结构和 非线性结构两大类,其中,非线性结构又可分为树结构和图结构。
图是比树结构更复杂的一种数据结构。在线性结构中,除首结点没有前驱、末尾结点没有后继外,一个结点只有唯一的一个直接前驱和唯一的一个直接后继。在树结构中,除根结点没有前驱结点外,其余的每个结点只有唯一的一个前驱(双亲) 结点和多个后继 (子树) 结点。而在图中,任意两个结点之间都可能有直接的关系,所以图中一个结点的前驱结点和后继结点的数目是没有限制的。
1、单源点最短路径
所谓单源点最短路径,是指给定带权有向图 G 和源点 v0,求从 v0 到 G 中其余各顶点的最短路径。迪杰斯特拉 (Dijkstra) 提出了按路径长度递增的次序产生最短路径的算法,其思想是:把网中所有的顶点分成两个集合 S 和 T,S 集合的初态只包含顶点 v0,T集合的初态为网中除 v0 之外的所有顶点。凡以 v0 为源点,已经确定了最短路径的终点并入 S集合中,顶点集合 T则是尚未确定最短路径的顶点的集合。按各顶点与 v0 间最短路径长度递增的次序,逐个把 T集合中的顶点加入到S集合中去,使得 v0 从到 S集合中各顶点的路径长度始终不大于从 v0 到T集合中各顶点的路径长度。
对于下图 所示的有向网,用迪杰斯特拉算法求解顶点 v0 到达其余顶点的最短路径的过程如表 3-1 所示。
终点 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
v1 | ∞ | ∞ | ∞ | ∞ | ∞ |
v2 | 100 ( v 0 , v 2 ) 100\\(v_0, v_2) 100(v0,v2) | 100 ( v 0 , v 2 ) 100\\(v_0, v_2) 100(v0,v2) | 90 ( v 0 , v 3 , v 2 ) 90\\(v_0,v_3,v_2) 90(v0,v3,v2) | 60 ( v 0 , v 3 , v 4 , v 2 ) 60\\(v_0,v_3,v_4,v_2) 60(v0,v3,v4,v2) | |
v3 | 30 ( v 0 , v 3 ) 30\\(v_0,v_3) 30(v0,v3) | 30 ( v 0 , v 3 ) 30\\(v_0,v_3) 30(v0,v3) | |||
v4 | ∞ | 60 ( v 0 , v 5 , v 4 ) 60\\(v_0,v_5,v_4) 60(v0,v5,v4) | 50 ( v 0 , v 3 , v 4 ) 50\\(v_0,v_3,v_4) 50(v0,v3,v4) | ||
v5 | 10 ( v 0 , v 5 ) 10\\(v_0,v_5) 10(v0,v5) | ||||
说明 | 在从v0到v1、v2、v3、v4、v5的路径中,(v0,v5)最短,则将顶点 v5加入S集合,并且更新 v0到v4的路径 | 在从v0到v1、v2、v3、v4的路径中,(v0,v3)最短,则将顶点 v3加入S集合,并且更新 v0到v2、 v0到v4的路径 | 在从v0的到v1、v2、v4的路径中,(v0,v3,v4)最短,则将顶点 v4加入S集合,并且更新 v0到v2的路径 | 在从v0的到v1、v2的路径中,(v0,v3,v4,v2)最短,则将顶点 v2加入S集合 | v0的到v1无路径 |
集合S | {v0, v5} | {v0, v5, v3} | {v0, v5, v3, v4} | {v0, v5, v3, v4, v2} |
为了能方便地求出从 v0 到 T 集合中各顶点最短路径的递增次序,算法实现时引入一个辅助向量 dist。它的分量 dist[i] 表示当前求出的从 v0 到终点 vi 的最短路径长度。这个路径长度并不一定是最后的最短路径长度。它的初始状态为: 若从 v0 到 vi 有弧,则 dist[i]为弧上的权值;否则,置 dist[i] 为 ∞。显然,长度为 dist[u]= min{dist[i] | vi∈ V(G)} 的路径就是从 v0 出发的长度最短的一条最短路径。此路径为(v0,u),这时顶点 u 应从集合T中删除,将其并入集合 S。
设图采用邻接矩阵 arcs 存储,那么每次选出一个顶点 u 并使之并入集合 S后,就根据情况修改T集合中各顶点的路径长度 dist。对于T集合中的某一个顶点 i 来说,其更短路径可能为(v0, …,vu, vi)。也就是说,若dist[u]+arcs[u][i]<dist[i],则修改 dist[i],使 dist[i]= dist[u]+arcs[u][i] 。
对 T集合中各顶点的 dist 进行修改后,再从中挑选出一个路径长度最小的顶点,从T集合中删除之并将其并入S集合。依此类推,就能求出源点到其余各顶点的最短路径长度。
2、每对顶点间的最短路径
若每次以一个顶点为源点,重复执行迪杰斯特拉算法 n 次,便可求得网中每一对顶点之间的最短路径。下面介绍弗洛伊德 (Floyd) 提出的求最短路径的算法,该算法在形式上要更简单一些。
弗洛伊德算法思想是:假设图采用邻接矩阵的方式存储,需要求从顶点 vi 到 vj 的最短路径。arcs[i][j] 表示弧 (vi,vj)的权值,若此弧不存在,则权值为区别于有效权值的一个数。如果存在 vi 到 vj 的弧,则从 vi 到 vj 存在一条长度为 arcs[i][j] 的路径,该路径不一定是最短路径,尚需进行n次试探。首先考虑路径 (vi,v0,vj)是否存在 (即判别路径 (vi,v0) 和 (v0,vj)是否存在),若存在,则比较 (vi,vj)与(vi,v0,vj)的路径长度,取较短者为从 vi 到 vj 且中间顶点的序号不大于0的最短路径。假如在路径上再增加一个顶点 vi,也就是说,如果 (vi,···,v1)和(v1,···,vj) 分别是当前找到的中间顶点的序号不大于 0的 vi 到 v1 以及 v1到vj 的最短路径,那么 (vi,···v1···,vj)就有可能是从 vi 到 vj 且中间顶点的序号不大于1 的最短路径。将它与已经得到的从 vi 到 vj 的中间顶点的序号不大于 0 的最短路径相比较,从中选出中间顶点的序号不大于 1 的最短路径之后,再增加一个顶点 v2 继续进行试探,依此类推。一般情况下,若(vi,···,vk)和(vk,···,vj) 分别是从vi 到vk和 vk 到 vj 的中间顶点的序号不大于k-1 的最短路径,则将(vi,···,vk,···,vj)与已经得到的从 vi 到 vj 的中间顶点的序号不大于 k-1 的最短路径相比较,长度较短者便是从 vi 到 vj 的中间顶点的序号不大于k的最短路径。这样,经过 n 次试探后,最后求得的必是从 vi 到 vj 的最短路径。按此方法,可以同时求得各对顶点间的最短路径。