所谓单源点最短路径,是指给定带权有向图 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]
若每次以一个顶点为源点,重复执行迪杰斯特拉算法 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 的最短路径。按此方法,可以同时求得各对顶点间的最短路径。