单源最短路径和广度优先搜索要做的事很像。
关于广度优先搜索可以看图算法这一篇笔记。
单源最短路径给定一个源s,当算法执行完毕,找出从源s到图中的每个顶点权重最小的一条路径。
其实广度优先搜索可以看作特殊情况的单源最短路径,在广度优先搜索解决的图中,所有的边权重都为1。
注意: 本篇笔记说的最短路径都是指权重最低的路径,并非通过的边数最少的路径
某些单源最短路径问题的算法中允许出现负权重的边,就比如我们要讨论的Bellman-Ford
算法。
但是有些算法不允许,比如Dijkstra算法。
单源最短路径问题中能不能存在环路呢?我们分情况讨论。
综上所述,算法最后找到的最短路径中应该不包含环路,即都是简单路径。
我们来看下单源最短路径算法的核心操作。松弛。
对于每个节点v,维持一个属性v.d
,这个属性用来维持源s到v的最短路径估值。它是s到v的一条最短路径权重的上界。
对于每个节点v,维持一个属性v.PI
,这个属性和d配合,用于记录当前的估值情况下和v连接的前驱节点。
提供一个权重函数w(u,v)
计算u和v构成的边的权重
我们给每个节点的d和PI初始值定为无穷和NIL。把源节点s的d定为0
INITIALIZE-SINGLE-SOURCE(G,s)
for 图G中的每个节点 v
v.d = ∞
v.PI = NIL
s.d = 0
提供松弛函数RELAX(u,v,w)
对边(u,v)
进行松弛,参数w是权重函数。
松弛函数其实就是测一下如果经过u的话会不会对节点v的最短路径估值d起到优化。在说通俗点就是测试连接(u,v)
的话会不会让当前s到v的最短路径估值更小。如果是就连接(u,v)
RELAX(u,v,w)
if v.d > u.d + w(u,v)
v.d = u.d + w(u,v)
v.PI = u
对溜,就是这样,如果当前节点v的估值大于u的估值加边(u,v)
的权重的话就重置v的估值为u.d+w(u,v)
并设置v的前驱节点为u。
我们假设sp(s,v)
是从源s到节点v的最短路径的权重
(u,v)
我们有sp(s,v) <= sp(s,u) + w(u,v)
v.d >= sp(s,v)
。v
的最短路径估值d,一旦发现更短的路径就更新它,直到v.d == sp(s,v)
后,松弛操作将永远找不到更短的路径,v.d
就永远不会更新了。v.d == sp(s,v) == ∞
。s?u→v
是图中的一条最短路径,并且在对边(u,v)
进行松弛前的任意时间有u.d == sp(s,u)
,则在之后的所有时间有v.d == sp(s,v)
。p=(v0,v1,...,vk)
是从源节点s=v0
到节点vk
的一条最短路径,并且对p中边的松弛次序为(v0,v1),(v1,v2)...(vk-1,vk)
,则vk.d == sp(s,vk)
。该性质的成立与其他任何松弛操作无关,即使这些松弛操作是和对p上的边所进行的松弛操作穿插进行的。v.d == sp(s,v)
则前驱子图是一颗以跟节点为s的最短路径树。不好意思题外话。。。??了个??,抄书好累啊,但是我不得不把算法导论翻译成人话,没错我在逼自己看懂它。。。
有了上面的分析,我们可以开始研究主角Bellman-Ford算法了
Bellman-Ford算法可以工作在不存在从源节点可以达到的权重为负值的环路的图。
如果存在这样的图,算法将返回False通知用户无法给出正确可靠的结果。
算法通过不断对每条边进行松弛来一点点的使每个节点v的最短路径估值d一点一点的下降,最终达到sp(s,v)
。
其实对于每一次对所有边挨个进行松弛操作,必定会有至少一条边的d值会被设置为正确的。这样只需要对图的每条边进行图中节点数减1次循环就可以计算出每个节点正确的最短路径估值。所以我们这样编写BELLMAN-FORD算法:
BELLMAN-FORD(G,w,s)
INITIALIZE-SINGLE-SOURCE(G,s)
for i = 1 to |G.V| - 1
for G中每个边 (u,v)
RELAX(u,v,w)
for G中每个边 (u,v)
if v.d > u.d + w(u,v)
return FALSE
return TRUE
But,,why???下面证明下。
假设图不包含从源节点s可以到达的权重为负值的环路,我们考虑每一个可以从源到达的节点v肯定有一条最短路径p={v1,v2,...,vk}
。s=v1,v=vk。由于我们前面的前驱子图性质,所以这个最短路径肯定是一个简单路径,即没有环。那么这条路径里就最多有图中节点数量-1个边。再根据前面的路径松弛性质,只要这条最短路径是按顺序松弛的,那么就可以得到正确的d值,和其它松弛操作的顺序无关,那么每次外层for循环都松弛所有的边,那么在第i次松弛操作时被松弛的边肯定包含(vi-1,vi)
,所以,v.d==vk.d==sp(s,vk)==sp(s,v)
d就是对的喽。
迄今为止,我们还有一个坑没有解决。那就是算法的返回值。
算法是否会在存在负权环路时返回FALSE,在正常情况下返回TRUE呢?
先看看算法导论的证明,确实很严谨很正确。不过我下次复习看这个笔记的时候。。。我这笨脑袋肯定还得想半天。
所以得想想怎么说才能让我下次一下就能懂。
emmm。。。看这。。。。图???假设有这么一个负权环,从上面的节点开始,假设初始状态那个顶上的节点的d是0,在第一圈到左下脚的节点时,还是满足v.d<=u.d+w(u,v)
的。不会返回FALSE,不过当它转了一圈后再来就有问题了。会产生v.d>u.d+w(u,v)
。而且随着圈越转越多这个差值会越来越离谱。
每次遍历所有边就必定要走一遍环路。所以到最后只需检查v.d>u.d+w(u,v)
即可。算法是正确的
原文:https://www.cnblogs.com/lilpig/p/12325832.html