由于这部分算法比较 trivial ,我就水个几篇文章。
在最短路径问题中,我们给定一个有向图
G=(V,E)
G
=
(
V
,
E
)
和权重函数
w:E→R
w
:
E
→
R
,该权重函数将每条边映射到实数值的权重上。
图中的一条路径
p=<v0, v1, ..., vk>
p
=<
v
0
,
v
1
,
.
.
.
,
v
k
>
的权重
w(p)
w
(
p
)
构成该路径的所有边的权重之和:
w(p)=∑ki=1w(vi−1, vi)
w
(
p
)
=
∑
i
=
1
k
w
(
v
i
−
1
,
v
i
)
定义从结点
u
u
到结点 的最短路径权重
δ(u, v)
δ
(
u
,
v
)
如下:
⎰ min{w(p):u⇝pv}
⎰
m
i
n
{
w
(
p
)
:
u
⇝
p
v
}
若存在
u
u
到 的路径
δ(u, v)=⎱ 0
δ
(
u
,
v
)
=
⎱
0
不存在路径
最短路径的几个变体:
单源最短路径问题:
找到给定结点
s
s
到 所有结点 的最短路径。
单目的地最短路径问题:
反向即为单源最短路径问题。
单节点对最短路径问题:
找到给定结点
u
u
到 给定结点 的最短路径。
(在该问题的已知所有算法中,最坏情况的复杂度和单源最短路径的复杂度一致)
所有点对最短路径问题:
对于所有结点对
u
u
和 ,找到结点
u
u
到 结点 的最短路径。
最短路径的最优子结构:最短路径的子路径也是最短路径。
首先,我们不妨假设在找到的最短路径中没有环路,即它们都是简单路径。
因此,我们可以将注意力集中到至多包含
|V|−1
|
V
|
−
1
条边的最短路径上。
此外,我们维护一个前驱结点
v.π
v
.
π
,该结点可能是另一个结点或者 NIL 。
定义 前驱子图
Gπ=(Vπ, Eπ)
G
π
=
(
V
π
,
E
π
)
Vπ={v∈V:v.π≠NIL} ⋃ {s}
V
π
=
{
v
∈
V
:
v
.
π
≠
N
I
L
}
⋃
{
s
}
Eπ={(v.π, v)∈E:v∈Vπ−{s} }
E
π
=
{
(
v
.
π
,
v
)
∈
E
:
v
∈
V
π
−
{
s
}
}
π
π
值具有如下性质:
在算法终止时,
Gπ
G
π
是一棵 最短路径树。
最短路径树 是一棵有根结点的树,该树包括了从源结点
s
s
到每个可以从 到达的结点的一条最短路径。
松弛操作
我们称
v.d
v
.
d
为
s
s
到 的最短路径估计。
初始化:
对一条边
(u, v)
(
u
,
v
)
的松弛过程为:测试是否可以将从
s
s
到 的最短路进行改善。(通过
u
u
来对 进行松弛操作)
松弛是唯一导致最短路径估计和前驱结点发生变化的操作。
本章所讨论的所有算法之间的不同之处是对每条边进行松弛的次数和松弛边的次序有所不同。
Dijkstra算法和用于有向无环图的最短路径算法对每条边仅松弛一次。
Bellman-Ford算法则对每条边松弛
|V|−1
|
V
|
−
1
次。
最短路径和松弛操作的性质:
三角不等式性质:
δ(s, v)≤δ(s, u)+w(u, v)
δ
(
s
,
v
)
≤
δ
(
s
,
u
)
+
w
(
u
,
v
)
上界性质:
v.d≥δ(s, v)
v
.
d
≥
δ
(
s
,
v
)
。一旦
v.d
v
.
d
的值达到
δ(s, v)
δ
(
s
,
v
)
,其值将不再发生变化。
非路径性质: 如果从
s
s
到 不存在路径,则总是有
v.d=δ(s, v)=∞
v
.
d
=
δ
(
s
,
v
)
=
∞
。
收敛性质: 如果
s⇝u→v
s
⇝
u
→
v
是一条最短路径,并且在对边
(u, v)
(
u
,
v
)
进行松弛前的任意时间有
u.d=δ(s, u)
u
.
d
=
δ
(
s
,
u
)
,则在之后的所有时间有
v.d=δ(s, v)
v
.
d
=
δ
(
s
,
v
)
。
(注:反之不成立,因为
δ(s, v)
δ
(
s
,
v
)
可能从别的路径更新)
路径松弛性质: 如果
p=<v0, v1, ..., vk>
p
=<
v
0
,
v
1
,
.
.
.
,
v
k
>
是从
s=v0
s
=
v
0
到
vk
v
k
的一条最短路径,并且我们对
p
p
中的边的松弛次序为 ,则
vk.d=δ(s, vk)
v
k
.
d
=
δ
(
s
,
v
k
)
。该性质的成立与任何其他的松弛操作无关,即使这些松弛操作是与对
p
p
上的边所进行的松弛操作穿插进行的。
前驱子图性质: 对于所有结点
v∈V
v
∈
V
,一旦
v.d=δ(s, v)
v
.
d
=
δ
(
s
,
v
)
,则前驱子图是一棵根结点为
s
s
的最短路径树。
本章概要
本章讨论的所有算法都假定有向图
G
G
以邻接链表的方式予以存放。此外,边的权重与边本身存放在一起,这样在遍历每条邻接链表时,我们可以在 时间内获得边的权重。
24.1
Bellman-Ford 算法
用于解决单源最短路径问题,并且返回一个布尔值,表明是否存在一个从源结点可以到达的权重为负值的环路。
通过对边进行松弛操作来渐进地降低从源结点
s
s
到每个结点 的最短路径估计值,知道该估计值与
δ(s, v)
δ
(
s
,
v
)
相同为止。
比较trivial,分为3个部分:
1. 初始化
distance[ ], vector<Edge>edges
d
i
s
t
a
n
c
e
[
]
,
v
e
c
t
o
r
<
E
d
g
e
>
e
d
g
e
s
;
2. 循环
|V|−1
|
V
|
−
1
次,每次循环松弛所有边。
3. 检查每条边,如果还可以松弛,说明有负环。
时间复杂度为
O(VE)
O
(
V
E
)
正确性证明:
1. 最短路长度
≤|V|−1
≤
|
V
|
−
1
,故由路径松弛性质即证、
2. 若存在负环
反证法,假设返回 true,
It′s trivial.
I
t
′
s
t
r
i
v
i
a
l
.
24.2
有向无环图中的单源最短路径问题