算法和数据结构之最短路径算法

Weighted Graphs

通常,我们发现沿着图中的一条边移动会有一些相关的成本(或利润),例如边的距离,汽油的价格,花费的时间等等。

我们称这之为加权图,称边值为权值。

我们将之前的图定义扩展如下:

加权图G由有序序列(V, E, W)组成,其中V和E是顶点和边,W是边的权值。

在这里插入图片描述
V={a, b, c, d, e}
E={(a, b), (a, c), (a, d), (a, e), (c, b), (d, b),(d, c), (e, d)}
W((a, b))=50, W((a, c))=30等等。

同时我们可以用图的邻接矩阵表示为:
在这里插入图片描述
“-”表示边缘不存在。

最短路径

与加权图有关的一个常见问题是寻找顶点之间的最短路径。

Dijkstra’s Algorithm

该算法的工作原理是将顶点分为两个集合,S和C。
在每个迭代;S包含已经被选择的节点集。
S也叫做选择集 selected set.

在每个迭代;C包含了一组尚未被选中的节点:
C也叫做候选集 candidate set.

在每一步中,我们将到达C的最小节点移动到S。

我们还需要一个函数D使D(ci)是我们目前找到的候选集合c中从顶点s到顶点ci的最短距离。

举个例子:
在这里插入图片描述

Step 0:

S={a}
C={b, c, d, e}
D()= 50, 30, 100, 10
我们现在选择D的最小值,D(e)=10。

Step 1:
我们将顶点e从C移动到S。
S={a, e}
C={b, c, d}
D()= 20, 30, 50
a到d有两条,一条为a-e-d:10+10=20,另一条是a-d=100,20<100所以我们不需要在D()中再加入100,后面出现这种情况同理。
我们现在选择D的最小值,D(d)=20。

Step 2:
将顶点d从C移动到S。
S={a, e, d}
C={b, c}
D()= 40, 30
我们现在选择D的最小值,D©=30。

Step 3:
将顶点c从c移动到S。
S={a, e, d, c}
C={b}
D()= 35 // a-c-b 30+5=35
我们现在选择D的最小值,D(b)=35

Step 4:
将顶点b从C移动到S
S={a, e, d, c, b}
C={}
C中没有候选顶点了,所以我们完成了。

结果: W(a)=0, W(e)=10, W(d)=20, W©=30, W(b)=35.

Bellman-Ford

适用于具有负边权值的图。

识别负循环和路径上有负循环的顶点。

为所有其他顶点找到正确的路径和路径长度。

举个例子:
在这里插入图片描述
上图中a-c-b组成了一个循环,这个循环的总权值为-1,这意味着如果按照刚才Dijkstra’s Algorithm来的话,所有路径都可以重复走这个cycle让自己权值降到最低。

因此我们如何用Bellman-Ford考虑呢?

与Dijkstra算法不同的是,Dijkstra算法在每次迭代时只更新最有希望的(下一个成本最低的)顶点,Bellman-Ford在每次迭代中更新每个顶点。
这意味着Bellman-Ford的每次迭代都比Dijkstra的迭代需要更多的工作。

拿上图举例:

它有6个顶点,所以我们将在主循环中运行5次。

初始化:
我们为D设置初始值:
D(s)=0

sabcde
0

迭代1:
顶点 s: 我们可以到达a和e, 更新。
顶点 a: 我们可以到达c, 更新。
顶点 b: 我们还不能到达,跳过。
顶点 c: 我们可以到达b, 更新。
顶点 d: 我们还不能到达,跳过。
顶点 e: 我们可以到达d, 更新。

下表为第一次迭代结束后的权值:

sabcde
010101238

迭代2:
顶点 s: 我们可以到达a和e, 没有变化。
顶点 a: 我们可以到达c, 没有变化。
顶点 b: 我们可以到达a,更新
顶点 c: 我们可以到达b, 没有变化。
顶点 d: 我们可以到达a和c,更新。
顶点 e: 我们可以到达d, 没有变化。

下表为第二次迭代结束后的权值:

sabcde
0-110238

注意:
a现在的权值是s-e-d-a=8-5-4=-1
c现在的权值是s-e-d-c=8-5-1=2
后面同理。

迭代3:
顶点 s: 我们可以到达a和e, 没有变化。
顶点 a: 我们可以到达c, 更新。
顶点 b: 我们可以到达a,没有变化。
顶点 c: 我们可以到达b, 更新。
顶点 d: 我们可以到达a和c,没有变化。
顶点 e: 我们可以到达d, 没有变化。

下表为第三次迭代结束后的权值:

sabcde
0-1-1138

迭代4:
顶点 s: 我们可以到达a和e, 没有变化。
顶点 a: 我们可以到达c, 没有变化
顶点 b: 我们可以到达a,更新。
顶点 c: 我们可以到达b, 没有变化。
顶点 d: 我们可以到达a和c,没有变化。
顶点 e: 我们可以到达d, 没有变化。

下表为第四次迭代结束后的权值:

sabcde
0-2-1138

迭代5:
顶点 s: 我们可以到达a和e, 没有变化。
顶点 a: 我们可以到达c, 更新。
顶点 b: 我们可以到达a,没有变化。
顶点 c: 我们可以到达b, 更新。
顶点 d: 我们可以到达a和c,没有变化。
顶点 e: 我们可以到达d, 没有变化。

下表为第五次迭代结束后的权值:

sabcde
0-2-2038

五次迭代结束,
检查:
顶点 s: 我们可以到达a和e, 没有变化。
顶点 a: 我们可以到达c, 把a标记为bad。
顶点 b: 我们可以到达a,没有变化。
顶点 c: 我们可以到达b, 没有变化。
顶点 d: 我们可以到达a和c,没有变化。
顶点 e: 我们可以到达d, 没有变化。

sabcde
0X-2038

结论:
我们可以得出结论,图包含一个包含顶点a的负成本循环。

如果我们从顶点a开始执行DFS(或BFS),将所有访问过的顶点标记为bad,我们得到以下结果:

sabcde
0XXX38

因此:

顶点 a: 通过循环到达。
顶点 b: 通过循环到达。
顶点 c: 通过循环到。
顶点 d: 可到达的花费为3。
顶点 e: 可到达的花费为8。

A*算法

公式:
f(n)=g(n)+h(n),

f(n) 是从初始状态经由状态n到目标状态的代价估计。
g(n) 是在状态空间中从初始状态到状态n的实际代价。
h(n) 是从状态n到目标状态的最佳路径的估计代价。
放在路径搜索问题中,状态就是图中的节点,代价就是距离或者权重。

A*的诀窍是找到一个好的启发式。

对顶点选择规则的启发式修改将Dijkstra算法变成了一个被称为A*算法的例子。

尽管在最坏的情况下,A*并不比Dijkstra更快,但在实践中,它通常代表着实质性的改进。

从v到g的估计最小路径长度H(v, g)与实际最小路径长度P(v, g)越接近,A*就越快找到解。

References

  1. Introduction to The Design and Analysis of Algorithms, A. Levitin, 3rd Ed., Pearson 2011.
  2. Introduction to Algorithms, T. H. Cormen, 3rd Ed, MIT Press 2009.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值