全部最短路径的表示方式
如果用树来表示图中点A到点B的最短路径,从每个叶子节点B,往上追溯可以获取到其中一个最短路径。即对树进行DFS遍历可以找到全部最短路径。
那么这个树会有什么特点?
1. 树的根点都是A,叶子节点是B
2. 叶子节点的父节点,即叶子节点B。(因为图全部边的权重>1)
3. 如果相同的节点层数不同,则将相同的节点从树中剥离形成的子树也满足上面两个特性。
比如下图就不合法,因为其子树(即节点1->3的最短路径树不满足特性2,不合法)
4. 如果相同的节点层数相同,其父节点链必定有一个节点不相同。
求单源最短路径和求单源全部最短路径的关系
(先去看单源最短路径算法)
单源最短路径算法的精髓在于松弛操作。将图节点S分成两部分,U和V。
-
(初始步)U的节点是已经求出最短路径的节点,初始为原点。
V的节点是还未求出最短路径的节点。 -
对每个节点 u ∈ U u \in U u∈U对每个节点 v ∈ V v \in V v∈V进行松弛操作。
-
挑选出当前最短的节点,加入U,从V中去掉。
-
如果U!=S,则继续进行2。
U中节点因为2操作一直保持着一个性质:
对于任意 u ∈ U u \in U u∈U,从原点A到节点u的最短距离已经求出。
为什么可以保持这个性质?
可以把U想像成一个长着很多毛的蛋,将要加入节点U的节点v,与U中某节点u的形成边(u,v)是所有蛋伸出的毛中最短的,进而推得u的最短路径节点序列后加v节点,就是v的最短路径。那么如果存在一个全局路径,使得 u i , v j , . . . v u_i,v_{j},...v ui,vj,...v,使得路径和小于边(u,v),根据权重大于0,可得边 ( u i , v j ) < ( u , v ) (u_i,v_j)< (u,v) (ui,vj)<(u,v),
那么边(u,v)就不是最短边了。跟假设边(u,v)是所有蛋伸出的毛中最短的是冲突的。
那么求一条最短路径是这样,求所有最短路径呢?
求一条和求多条最后求出的最短路径的值都是一样的。最短路径变多了。
原来一条的时候,假如将v加入集合U的时候,原点到v的最短路径就求出来了,即
u的最短路径 ( u s , u i , u i + 1 , . . . u ) (u_s,u_i,u_{i+1},...u) (us,ui,ui+1,...u)后面加上节点v, ( u s , u i , u i + 1 , . . . u , v ) (u_s,u_i,u_{i+1},...u,v) (us,ui,ui+1,...u,v)为源点 u s − > v u_s->v us−>v的最短路径。
那么如果节点v加入U时,有假设 u s − > u u_s->u us−>u的所有最短路径已经求出来了,那么如果所有最短路径用树表示的话,就将u的最短路径树的叶子节点下加v组成树A,和v当前的最短路径树B做合并(操作有点麻烦),就可以求出到v的所有最短路径。
此时将v加入U,则可以保持对于任意 u ∈ U u \in U u∈U,u的全部路径已经求解完毕的性质。
下面来证明下假设:
对于任意 u ∈ U u \in U u∈U,u的全部最短路径都已经求出来了。
还是利用反证法,如果v要加入集合U,有没有可能存在v的全部最短路径包含U之外(即集合V中)的节点?还是看长毛蛋的证明,如果最短路径有V中的节点,那么v就不是离蛋最近的节点。最短路径不含V中节点,那么节点v的全部最短路径都由当前U中的节点组成(彼时彼刻,恰如此时此刻)。
最短路径树怎么演化?
松弛命中是什么?
D
u
+
w
(
u
,
v
)
<
D
v
D_u+w(u,v)<D_v
Du+w(u,v)<Dv
D
u
为
节
点
u
的
最
短
距
离
,
w
(
u
,
v
)
是
边
(
u
,
v
)
权
重
D_u为节点u的最短距离,w(u,v)是边(u,v)权重
Du为节点u的最短距离,w(u,v)是边(u,v)权重
在求一条最短路径时,如果出现松弛命中,那么你会用u的最短路径后加节点v。但是求多条就不是了。
此时松弛命中会出现两种情况:
i) D u + w ( u , v ) < D v D_u+w(u,v)<D_v Du+w(u,v)<Dv
这种情况下,松弛操作计算出经过节点u,可以使到节点v路径更短。直接在u的最短路径树上加v叶子。
ii) D u + w ( u , v ) = D v D_u+w(u,v) = D_v Du+w(u,v)=Dv
出现相同的最短路径,这个时候就麻烦,得合并树。
比如
其中原点1到4的全部最短路径为:
树合并操作是比较麻烦的,之前全部最短路径为:
1->2->7->6
1->3->5->7->6
4对6松弛产生产生了同样短的路径,其路径为:
1->2->4->6
1->3->5->4->6
这个在草稿纸上画一画,就能写出这个树,可以咋做成算法? 化成图用树存储确实看的明白点,但是用树存就很不香了,要用一个二维数组。每行存一条路径。
这个时候,4对6做松弛,就把4的所有路径后面加6,放入到6的最短路径集合中。
伪码
还是Dijstra算法,只不过对松弛函数做点小小的改变。
- (初始步)U的节点是已经求出最短路径的节点,初始为原点。
V的节点是还未求出最短路径的节点。
令源点 u s u_s us的所有最短路径为 u s u_s us,其他节点所有最短路径为 ( u s , v i ) (u_s,v_i) (us,vi)
- 对每个节点 u ∈ U u \in U u∈U对每个节点 v ∈ V v \in V v∈V进行松弛操作。
i) 如果松弛未命中,则不做任何操作。
ii) 如果松弛命中(产生更小路径),则将u所有最短路径后添加v,将结果替换为v所有最短路径。
iii) 如果松弛命中(产生等长路径),则将u的所有路径后添加v,将结果追加到为v所有最短路径。