为了账号安全,请及时绑定邮箱和手机立即绑定

数据结构(十一):最短路径(Bellman-Ford算法)

标签:
数据结构

webp

最短路径是指连接图中两个顶点的路径中,所有边构成的权值之和最小的路径。之前提到的广度优先遍历图结构,其实也是一种计算最短路径的方式,只不过广度遍历中,边的长度都为单位长度,所以路径中经过的顶点的个数即为权值的大小。

最短路径中不能包含负权回路,因为每次经过负权回路,路径的权值会减少,所以这种情况下不存在最短路径。有些图结构中会存在负权边,用于表达通过某条途径可以降低总消耗,在有向图中,负权边不一定会形成负权回路,所以在一些计算最短路径算法中,负权边也可以计算出最短路径;在无向图中,负权边就意味着负权回路,所以无向图中不能存在负权边。后续的所有讨论都设定图中不存在负权回路的情况。

松弛函数

对边集合 E 中任意边,以 w(u,v) 表示顶点 u 出发到顶点 v 的边的权值,以 d[v] 表示当前从起点 s 到顶点 v 的路径权值

若存在边 w(u,v),使得:

d[v] \gt d[u]+w(u,v)

则更新 d[v] 值:

d[v]=d[u]+w(u,v)

所以松弛函数的作用,就是判断是否经过某个顶点,或者说经过某条边,可以缩短起点到终点的路径权值。

为什么将缩短距离的操作称之为“松弛”,不妨理解为,选择某种方式后,到达目的的总代价降低了。什么名字无关紧要,不必纠结。

松弛函数代码示例
def releax(edge, distance, parent):    if distance[edge.begin - 1] == None:
        pass
    elif distance[edge.end - 1] == None or distance[edge.end - 1] > distance[edge.begin - 1] + edge.weight:
        distance[edge.end - 1] = distance[edge.begin - 1] + edge.weight
        parent[edge.end - 1] = edge.begin - 1
        return True    return False

distance 列表存储从起点到当前顶点的路径权值,parent 列表存储到当前顶点的前驱顶点下标值。初始 distance 列表和parent 列表元素皆为 None,表示路径权值为无穷大,处于不可达状态。

松弛函数执行次数

以对边集合 E 中每条边执行一次松弛函数作为一次迭代,接下来判断需要执行多少次迭代,可以确保计算出起点到每个顶点的最短距离。

webp

digraph

以图 digraph 为例,各顶点之间边的长度如图中所示。以 d[v] 表示起点 s 到顶点 v 的距离,以 \delta(s,v) 表示起点 s 到顶点 v 的最短路径权值,以 p=\langle v_0,v_1,...,v_k \rangle 表示从顶点 v_0 到顶点 v_k 的路径。初始情况 d[a]=0d[v]=\infty ,v \in V - \{a\}

digraph 图中可以明显发现,若已知 d[b],则对边 w(b,c) 执行松弛函数后,即可更新 d[c] 的值。若已更新 d[c] 的值,则对边 w(c,d) 执行松弛函数后,即可更新 d[d] 的值。

最好情况下分析:

若遍历松弛的边顺序为:w(a,b),w(b,c),w(c,d),其他两条边 w(a,c),w(a,d) 顺序无影响

第一次迭代:

对边 w(a,b) 执行松弛函数,则 d[b]=d[a]+w(a,b)=1
对边 w(b,c) 执行松弛函数,则 d[c]=d[b]+w(b,c)=3
对边 w(c,d) 执行松弛函数,则 d[d]=d[c]+w(c,d)=8

因为图结构比较简单,所以可以直接由观察得知,经过第一次迭代,即得出从起点到各个顶点的最短路径权值。

最坏情况下分析:

若遍历松弛的边顺序为:w(c,d),w(b,c)w(b,c),w(c,d),其他三条边 w(a,b),w(a,c),w(a,d) 顺序无影响

第一次迭代:

对边 w(c,d) 执行松弛函数,则 d[d]=\infty
对边 w(b,c) 执行松弛函数,则 d[c]=\infty
对边 w(a,b) 执行松弛函数,则 d[b]=d[a]+w(a,b)=1
对边 w(a,c) 执行松弛函数,则 d[c]=d[a]+w(a,c)=6
对边 w(a,d) 执行松弛函数,则 d[d]=d[a]+w(a,d)=10

第一次迭代,有三条边起到了松弛的效果,直观的可以看出 d[b]=\delta(a,b),第一次迭代可以获得经过一个顶点的最短路径,路径为 p=\langle a,b \rangle

第二次迭代:

对边 w(c,d) 执行松弛函数,则 d[d]=10
对边 w(b,c) 执行松弛函数,则 d[c]=d[b]+w(b,c)=3
对边 w(a,b) 执行松弛函数,则 d[b]=1
对边 w(a,c) 执行松弛函数,则 d[c]=6
对边 w(a,d) 执行松弛函数,则 d[d]=10

第二次迭代,有一条边起到了松弛的效果,直观的可以看出 d[c]=\delta(a,c),第二次迭代可以获得经过两个顶点的最短路径,路径为 p=\langle a,b,c \rangle

第三次迭代:

对边 w(c,d) 执行松弛函数,则 d[d]=d[c]+w(c,d)=8
对边 w(b,c) 执行松弛函数,则 d[c]=3
对边 w(a,b) 执行松弛函数,则 d[b]=1
对边 w(a,c) 执行松弛函数,则 d[c]=6
对边 w(a,d) 执行松弛函数,则 d[d]=10

第三次迭代,有一条边起到了松弛的效果,直观的可以看出 d[d]=\delta(a,d),第三次迭代可以获得经过三个顶点的最短路径,路径为 p=\langle a,b,c,d \rangle,路径如下图所示:

webp

迭代次数分析:

为了方便后续讨论,对于顶点 v,若已确定 d[v]=\delta(s,v),即已确定从起点到该顶点的最短路径权值,这里不妨称该顶点 v 为已确认顶点。后续以路径长度表示从起点到顶点 v 的路径上,经过的顶点个数。例如,对于路径 p=\langle s,v_1,v_2,v \rangle,路径权值为 d[v],路径长度为 3。

通过前面的示例过程可以推论:

若图中存在未确认的顶点,则对边集合的一次迭代松弛后,会增加至少一个已确认顶点

推论的意思是指,对图中顶点的确认,是以一种波纹扩散的方式进行的,这里增长的扩散半径是指路径中已确认顶点的个数,而不是路径的权值,并且路径中不包含未确认顶点。

注意路径长度和路径权值并无绝对关系,例如若 \delta(s,v_1) 的最短路径为 p_1=\langle s,v_1 \rangle\delta(s,v_2) 的最短路径为 p_2=\langle s,v_1,v_2\rangle,虽然 p_2 的路径长度大于 p_1,但是 \delta(s,v_2) 的权值并不一定大于 \delta(s,v_1)。例如上图中,若边 w(b,c) 的权值为 -2 而不是 2,则 \delta(a,c) 的值为 -1,而不是 3,即路径 \langle a,b,c \rangle 的权值要小于路径 \langle a,b \rangle

推论证明

初始情况下,只有起点 s 属于已确认顶点,根据邻接表记录,若起点存在相邻顶点,则对边集进行一次迭代松弛后,会增加至少一个已确认顶点。

webp

反证说明:

若一次迭代后,起点 s 的所有相邻顶点都仍处于未确认状态,即一次迭代后,对于任意相邻顶点 v,存在 d[v] \gt \delta(s,v)

则对于任意由起点 s 出发到相邻顶点 v_i 的路径 p_i=\langle s,v_i \rangle,存在由起点 s 出发经过相邻顶点 v_j 到达顶点 v_i 的路径 p_j=\langle s,v_j,...,v_i \rangle,使得路径 p_j 的权值小于路径 p_i 的权值,即可以减小 d[v_i] 的值。

  • j = i,则 p_j=\langle s,v_i,...,v_i \rangle,路径 p_j 的权值小于路径 p_i 的权值,说明路径 \langle v_i,...,v_i \rangle 权值为负数,即图中存在负权回路,与讨论背景不符,如下图所示。

webp

  • j \neq i,对于路径 p_j=\langle s,v_j,...,v_i \rangle 中的 \langle s,v_j \rangle 部分,因为对于任意相邻顶点 v,存在 d[v]>\delta(s,v),所以同样存在由起点 s 出发经过相邻顶点 v_k 到达顶点 v_j 的路径 p_k=\langle s,v_k,...,v_j \rangle,使得路径 p_k 的权值小于路径 \langle s,v_j \rangle 的权值,更新路径 p_jp_j=\langle s,v_k,...,v_j,...,v_i \rangle。如此重复,若起点 s 的相邻顶点不为无穷多,则必然在某一时刻,更新路径 p_jp_j=\langle s,v_i...,v_k,...,v_j,...,v_i \rangle,使得路径 p_j 的权值小于路径 p_i 的权值,即图中存在负权回路,与讨论背景不符,如下图所示。

webp

所以对于初始状态的起点 s 而言,执行一次迭代后,至少会增加一个已确认顶点。

一般性的,当图中已经存在一个或多个已确认顶点时,即图处于任意一种状态,若图中尚存在未确认顶点,则执行一次迭代后,会增加至少一个已确认顶点。

证明过程与上面类似,使用下图作为辅助说明:

webp

example



作者:zhipingChen
链接:https://www.jianshu.com/p/b876fe9b2338


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消