算法复习——图算法篇之单源最短路径之Dijkstra算法

算法复习——图算法篇之单源最短路径之Dijkstra算法

以下内容主要参考中国大学MOOC《算法设计与分析》,墙裂推荐希望入门算法的童鞋学习!

1. 问题背景

​ 如何计算带权图中源点到所有其他顶点的最短路径

2. 问题定义

单源最短路径问题(边权为正)(Single Source Shortest Paths Problem with Positive Weights)

输入:

  • 带权图 G = < V , E , W > G=<V, E, W> G=<V,E,W>,其中 w ( u , v ) ≥ 0 w(u, v) \geq 0 w(u,v)0(圈中所有边权为正), ( u , v ) ∈ E (u, v) \in E (u,v)E
  • 源点编号 s s s

输出:

  • 源点 s s s到所有其他顶点 t t t的最短距离 δ ( s , t ) \delta(s, t) δ(s,t)和最短路径 < s , … , t > <s, \dots, t> <s,,t>

3. 算法思想

  • 辅助数组
    • d i s t dist dist表示距离上界(估计距离)
      • 源点 s s s d i s t [ s ] = 0 dist[s]=0 dist[s]=0;其他顶点 u u u d i s t [ u ] dist[u] dist[u]初始化为∞
      • d i s t [ u ] dist[u] dist[u]:源点 s s s到顶点 u u u的距离上界, δ ( s , u ) ≤ d i s t [ u ] \delta(s, u) \leq dist[u] δ(s,u)dist[u]
    • c o l o r color color表示顶点状态
      • 黑色:到顶点 u u u最短路已被确定
      • 白色:到顶点 u u u最短路尚未确定
    • p r e d pred pred表示前驱顶点
      • ( p r e d [ u ] , u ) (pred[u], u) (pred[u],u)为最短路径上的边
  • 核心思想
    • 步骤1:新建空的黑色顶点集 V A V_A VA
    • 步骤2:选择一个白色顶点变为黑色(到该顶点最短路被确定)
    • 步骤3:重复步骤2,直至所有顶点均为黑色

​ 针对这个核心思想,可以提出以下两个问题:

  • 问题1:选择哪个白色顶点变为黑色?采用贪心策略

    • 对每个顶点 y ∈ V − V A y \in V-V_A yVVA,都有一个估计距离 d i s t [ y ] dist[y] dist[y]
    • 选择估计距离最小的顶点 v v v d i s t [ v ] ≤ d i s t [ y ] dist[v] \leq dist[y] dist[v]dist[y] v , y ∈ V − V A v,y \in V-V_A v,yVVA
  • 问题2:如何更新每个顶点的估计距离?

    • 当前到顶点 v v v的最短路径: < s , w , v > <s, w, v> <s,w,v>,距离为 d i s t [ v ] dist[v] dist[v]
    • 通过顶点 u u u的新路径: < s , … , u , v > <s, \dots, u, v> <s,,u,v>,距离为 d i s t [ u ] + w ( u , v ) dist[u]+w(u, v) dist[u]+w(u,v)
    • 如果新路径更短( d i s t [ u ] + w ( u , v ) < d i s t [ v ] dist[u]+w(u, v)<dist[v] dist[u]+w(u,v)<dist[v]
      • 更新 d i s t [ v ] dist[v] dist[v] d i s t [ v ] = d i s t [ u ] + w ( u , v ) dist[v]=dist[u]+w(u, v) dist[v]=dist[u]+w(u,v)
      • 该操作被称为松弛操作

4. 伪代码

Dijkstra(G, S)

输入:图G=<V, E, W>,源点​s​

输出:单源最短路径P

新建一维数组color[1..|V|],dist[1..|V|],pred[1..|V|]
// 初始化
for u ∈ V do
	color[u] ← WHITE
	dist[u] ← ∞
	pred[u] ← NULL
end
dist[s] ← 0
// 执行单源最短路径算法
for i ← 1 to |V| do
	minDist ← ∞
	rec ← 0
	for j ← 1 to |V| do
		if color[j] != BLACK and dist[j] < minDist then
			minDist ← dist[j]
			rec ← j
		end
	end
	for u ∈ G.Adj[rec] do
		if dist[rec] + w(rec, u) < dist[u] then
			dist[u] ← dist[rec] + w(rec, u)
			pred[u] ← rec
		end
	end
	color[rec] ← BLACK
end

​ 通过这种方法实现的时间复杂度是 O ( ∣ V ∣ 2 ) O(|V|^2) O(V2)

5. 优先队列优化

​ 但很显然我们可以通过优先队列,来加速查询。优先队列部分可参考算法复习——图算法篇之最小生成树之Prim算法

Dijkstra-PriQueue(G, s)

输入:图G=<V, E, W>,源点s

输出:单源最短路径P

新建一维数组color[1..|V|],dist[1..|V|],pred[1..|V|]
新建空优先队列Q
// 初始化
for u ∈ V do
	color[u] ← WHITE
	dist[u] ← ∞
	pred[u] ← NULL
end
dist[s] ← 0
Q.Insert(V, dist)
// 执行单源最短路径算法
while 优先队列Q非空 do
	v ← Q.ExtractMin()
	for u ∈ G.Adj[v] do
		if dist[v] + w(u, v) < dist[u] then
			dist[u] ← dist[v] + w(u, v)
			pred[u] ← v
			Q.DecreaseKey((u, dist[u]))
		end
	end
	color[v] ← BLACK
end

​ 所以经过优化后,该算法的时间复杂度是 O ( ∣ E ∣ l o g ∣ V ∣ ) O(|E|log|V|) O(ElogV)

6. 算法正确性证明

  • 定理:Dijkstra算法中,顶点 u u u被添加到 V A V_A VA时, d i s t [ u ] = δ ( s , u ) dist[u]=\delta(s, u) dist[u]=δ(s,u)

  • 证明:

    • 采用反证法,假设Dijkstra算法将顶点 u u u添加到 V A V_A VA时, d i s t [ u ] ≠ δ ( s , u ) dist[u] \neq \delta(s, u) dist[u]=δ(s,u)

    • 由于 d i s t [ u ] dist[u] dist[u]作为 δ ( s , u ) \delta(s, u) δ(s,u)的上界,故 d i s t [ u ] > δ ( s , u ) dist[u] > \delta(s, u) dist[u]>δ(s,u)

    • 应存在一条长度为 δ ( s , u ) \delta(s, u) δ(s,u)的从 s s s u u u的最短路径,不妨设其为 < s , … , x , y , … , u > <s, \dots, x, y, \dots, u> <s,,x,y,,u>,其中边 ( x , y ) (x, y) (x,y)横跨 < V A , V − V A > <V_A, V-V_A> <VA,VVA> x ∈ V A x \in V_A xVA y ∈ V − V A y \in V - V_A yVVA

在这里插入图片描述

  • 算法令 V A V_A VA中的顶点满足: d i s t [ x ] = δ ( x , s ) dist[x]=\delta(x, s) dist[x]=δ(x,s) x ∈ V A x \in V_A xVA

  • < s , … , x , y > <s, \dots, x, y> <s,,x,y>是最短路径 < s , … , x , y , … , u > <s, \dots, x, y, \dots, u> <s,,x,y,,u>的子路径,故:

    • δ ( s , y ) = δ ( s , x ) + w ( x , y ) = d i s t [ x ] + w ( x , y ) \delta(s, y)=\delta(s, x)+w(x, y)=dist[x]+w(x, y) δ(s,y)=δ(s,x)+w(x,y)=dist[x]+w(x,y)(公式1)
  • 算法对顶点 x x x出发的所有边(包括边 ( x , y ) (x, y) (x,y))已进行松弛操作,故:

    • d i s t [ y ] ≤ d i s t [ x ] + w ( x , y ) dist[y] \leq dist[x] + w(x, y) dist[y]dist[x]+w(x,y)(公式2)
  • 合并上述公式1和公式2,可得 d i s t [ y ] = δ ( s , y ) dist[y] = \delta(s, y) dist[y]=δ(s,y)

  • 最短路径 s , … , x , y , … , u > s, \dots, x, y, \dots, u> s,,x,y,,u>中, y y y出现在 u u u之前,故:

    • d i s t [ u ] > δ ( s , u ) ≥ δ ( s , y ) = d i s t [ y ] dist[u] > \delta(s, u) \geq \delta(s, y) = dist[y] dist[u]>δ(s,u)δ(s,y)=dist[y]
  • d i s t [ u ] > d i s t [ y ] dist[u] > dist[y] dist[u]>dist[y] u ≠ y u \neq y u=y u u u不应是下一个被添加顶点,故产生矛盾

​ 本质上,我们是证明了定理的逆否命题,若 d i s t [ u ] ≠ δ ( s , u ) dist[u] \neq \delta(s, u) dist[u]=δ(s,u),就不应该是 u u u被添加到 V A V_A VA,并使用了反证法。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值