最短路径
本节我们介绍一下如何在一个图中求最短路径。
1. 最短路径
假若要在计算机上建立一个交通咨询系统,则可以采用图的结构来表示实际的交通网络。如下图7.33所示,图中顶点表示城市,边表示城市间的交通联系。这个咨询系统可以回答旅客提出的各种问题。例如,一位旅客要从A城到B城,他希望选择一条途中中转次数最少的路线。假设图中每一站都需要换车,则这个问题反映到图上就是要找一条从顶点A到B所含边的数目最少的路径。
我们只需从顶点A出发对图作广度优先搜索,一旦遇到B就终止。由此所得广度优先生成树上,从根顶点A到顶点B的路径就是中转次数最少的路径,路径上A与B之间的顶点就是途径的中转站数,但是,这只是一类最简单的图的最短路径问题。有时,对于旅客来说,可能更关心的是节省交通费用;而对于司机来说,里程和速度则是他们感兴趣的信息。为了在图上表示有关信息,可对边赋予权,权的值表示两城市间的距离,或途中所需时间,或交通费用等等。此时路径长度的度量就不再是路径上边的数目,而是路径上边的权值之和。考虑到交通图的有向性(如航运,逆水和顺水时的船速就不一样),本节将讨论带权有向图,并称路径上的第一个顶点为源点
(Source),最后一个顶点为终点
(Destination)。下面讨论两种最常见的最短路径问题。
1.1 从某个源点到其余各顶点的最短路径
我们先来讨论单源点的最短路径问题:给定带权有向图G和源点v,求v到G中其余各顶点的最短路径。
例如,图7.34所示带权有向图G6中从v0到其余各定点之间的最短路径,如图7.35所示。从图中可见,从v0到v3有两条不同的路径: (v0,v2,v3)和(v0,v4,v3),前者长度为60,而后者长度为50。因此,后者是从v0到v3的最短路径;而从v0到v1没有路径。
如何求得这些路径?迪杰斯特拉(Dijkstra)提出了一个按路径长度递增的次序产生最短路径的算法。
首先,引进一个辅助向量D,它的每个分量D[i]表示当前所找到的从始点v
到每个终点vi
的最短路径的长度。它的初态为:若从v到vi有弧,则D[i]为弧上的权值;否则置D[i]为∞
。显然,长度为
D[j] = Min{D[i] | vi ∈ V} i
的路径就是从v出发的长度最短的一条最短路径。此路径为(v,vj)。
那么,下一条长度次短的最短路径是哪一条呢?假设该次短路径的终点是vk
,则可想而知,这条路径或者是(v,vk),或者是(v,vj,vk)。它的长度或者是从v到vk的弧上的权值,或者是D[j]和从vj到vk的弧上额权值之和。
一般情况下,假设S为已求得最短路径的终点的集合,则可以证明:下一条最短路径(设其终点为x)或者是弧(v,x),或者是中间只经过S中的顶点而最后到达顶x的路径。这可以用反证法来证明。假设此路径上有一个顶点不在S中,则说明存在一条终点不在S而长度比此路径短的路径。但是,这是不可能的。因为我们是按照路径长度递增的次序来产生各最短路径的,故长度比此路径短的所有路径均已产生,它们的终点必定在S中,即此假设不成立。
因此,在一般情况下,下一条长度次短的最短路径长度必是:
D[j] = Min{D[i] | vi ∈ V-S} i
其中,D[i]或者是弧(v,vi)上的权值,或者是D[k]
(vk ∈ S)和弧(vk,vi)上的权值之和。
根据以上分析,可以得到如下描述的算法:
1) 假设使用带权的邻接矩阵arcs来表示带权有向图,arcs[i][j]表示弧<vi,vj>上的权值。若<vi,vj>不存在,则置arcs[i][j]为∞
(在计算机上可用允许的最大值代替)。S为已找到从v出发的最短路径的终点的集合,它的初始状态为空集。那么从v出发到图上其余各定点(终点)vi可能达到的最短路径长度的初值为:
D[i] = arcs[LocateVex(G,v)][i] vi ∈ V
2) 选择vj,使得
D[j] = Min{D[i] | vi ∈ V-S}
vj就是当前求得的一条从v出发的最短路径的终点。令
S = S U {j}
3) 修改从v出发到集合V-S上任一顶点vk可达的最短路径长度。如果
则修改D[k]为
D[k] = D[j] + arcs[j][k]
4) 重复操作2)、3)共n-1次,由此求得从v到图上其余各顶点的最短路径是依路径长度递增的序列。
算法7.15为用C语言描述的迪杰斯特拉算法:
例如,图7.34所示有向网G6的带权邻接矩阵为:
若对G6施行迪杰斯特拉算法,则所得从v0到其余顶点的最短路径,以及运算过程中D向量的变化状况,如下所示:
我们分析这个算法的运行时间。第一个FOR循环的时间复杂度是O(n),第二个FOR循环共进行n-1次,每次执行的时间是O(n)。所以总的时间复杂度是O(n^2)。如果用带权的邻接表作为有向图的存储结构,则虽然修改D的时间可以减少,但由于在D向量中选择最小分量的时间不变,所以总的时间复杂度仍为O(n^2)。
人们可能只希望找到从源点到某一特定的终点的最短路径,但是,这个问题和求源点到其他所有顶点的最短路径一样复杂,其时间复杂度也是O(n^2)。
1.2 每一对顶点之间的最短路径
解决这个问题的一个办法是:每次以一个顶点为源点,重复执行迪杰斯特拉算法n次。这样,便可求得每一对顶点之间的最短路径。总的执行时间为O(n^3)。
这里要介绍由弗洛伊德(Floyd)提出的另一个算法。这个算法的时间复杂度也是O(n^3),但形式上简单些。
弗洛伊德算法仍从图的带权邻接矩阵cost出发,其基本思想是:
假设求从顶点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的最短路径。假如在路径上再增加一个顶点v1,也就是说如果(vi, …, v1)和(v1, …, vj)分别是当前找到的中间顶点的序号不大于0的最短路径,那么(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的最短路径。按此方法,可以同时求得各对顶点间的最短路径。
由此,可得算法7.16
:
例如,利用上述算法,可求得图7.36
所示带权有向图G7的每一对顶点之间的最短路径及其路径长度如图7.37
所示。
[参看]: