背景:
在图的BFS中我整理了用BFS求最短路径的方法,但是前提是这个图必须是无权的,但是不是所有图的路径距离都是无权的(单位权值),甚至大多数的图都是有权的,因为很多都是联系实际的问题,而在现实中,两个点之间距离是有权值的,比如实际上的距离,比如时间、成本、罚款、损失等等等等,都可以作为权值,所以我们需要解决带权值的最短路问题。
最短路径问题:
分单源最短路径和多源最短路径:
我着重学习了一下单源最短路。
即给定一个图G=(V , E),找到从给定源结点s∈V到每个结点v∈V的最短路径。
而单源最短路径问题可以解决许多其他问题:
最短路径的变体问题:
单目的地最短路径问题:找到从每个结点v到给定目的地结点t的最短路径。如果将图的每条边的方向翻转过来,就可以将此问题转换为单源最短路径问题。
单结点对最短路径问题:找到从给定结点u到给定结点v的最短路径。如果解决了针对单个结点u的单源最短路径问题,那么也就解决了这个问题。
所有结点对最短路径问题:对于每个结点u和v,找到从结点u到结点v的最短路径。
一个性质:最短路径的子路径也是最短路径。
负权重的边:
某些单源最短路径问题可能包括权重为负值的边,那么这决定了我们之后用什么方法来解决此类问题。因为有了负权重值并且图中有环路,那么定义最短路径权重δ(s,v)就是无定义了,因为我们就可以沿着任何”最短“路径再遍历一次权重为负值的环路,则可以找到一条权重更小的路径。则此时的δ(s,v) = -∞。但是只要没有环路,就是有定义的了,这时就可以用Bellman-Ford算法。而Dijkstra只能解决非负权值最短路问题。
最短路径的表示:
有时候,我们不但要计算出最短路径,还要把最短路径的结点给表示出来。
这要求我们对于每个结点v,我们维持一个 前驱结点 v.π。该前驱结点可能是另一个结点或NULL。这样,将从结点v开始的前驱结点链反转过来,就是从s到v的一条最短路径。
由π值所诱导的前驱子图Gπ = (Vπ,Eπ),我们定义结点集Vπ为图G中的前驱结点不为NULL的结点的集合,再加上源结点s,即
Vπ = {v∈V:v.π≠NIL}∪{s}
有向边集合Eπ是由Vπ中的结点的π值所诱导的边的集合,即
Eπ = {(v.π,v)∈E:v∈Vπ-{s}}
在算法终止时,Gπ是一棵”最短路径树“。这棵树是一棵有着根结点的树,它包括了从源结点s到每个可以从s到达的结点的一条最短路径。
它是一个有向子图G’ = (V’ , E’),并满足:
1. V’是图G中从源结点s可以到达的所有结点的集合。
2. G’形成一棵根结点为s的树。
3. 对于所有的结点v∈V’,图G’中从结点s到结点v的唯一简单路径是图G中从结点s到结点v的一条最短路径.
最短路径不一定是唯一的,最短路径树也不一定是唯一的
松弛操作
对于每个结点v来说,维持一个属性v.d,用来记录从源结点s到结点v的最短路径权重的上界。称v.d为s到v的最短路径估计。
初始化操作:
初始化对于所有的结点v属于V,我们都有v.π = NIL,s.d = 0,对于所有结点v∈G.V-{s},我们都有v.d = ∞。
伪代码如下:
INITIALIZE-SINGLE-SOURCE(G,s)
for each vertex v∈G.V
v.d = ∞
v.π = NIL
s.d = 0
松弛过程:
首先测试一下是否可以对从s到v的最短路径进行改善。
方法:将从结点s到结点u之间的最短路径距离加上结点u与v之间的边权重,并与当前的s到v的最短路径估计进行比较,如果前者小,则进行对v.d和v.π的更新。
伪代码如下:
RELAX(u,v,w)
if(v.d>u.d+w(u,v)
v.d = u.d+w(u,v)
v.π = u
《算法导论》中单源最短路径的概述的关键部分就是这些,值得反复理解。