算法描述

Dijkstra算法(狄杰斯特拉算法)
一种基于广度优先搜索和贪婪思想的求解 无负边有向赋权图单源最短路径 的算法。
算法将所有顶点区分为「到源点 ss 的最短距离」(以后简称「距离」) 已确定未确定 的顶点。算法开始前所有顶点的距离均未确定(一般置为InfinityInfinity),初始时置 ss 的距离为 0。以一个while循环查询当前 是否有距离未确定 的顶点,若有则将其中距离最小者 vv 选为 当前顶点,并使其距离已知。然后以BFS的方式松弛 vv 的邻接顶点 ww 并更新 ww 的前驱为 vv。当不再有未确定距离的顶点时算法结束,此时每一个顶点的距离均最小。通过递归寻找节点的前驱可以得到 ss 到该顶点的具体的最短路径。

松弛操作 是最短路径算法的关键,在确定当前顶点 vv (最新成为已确定距离的顶点) 后立即操作,目的是更新 vv 的邻接顶点 ww 的距离和其前驱。如下图,ss 经过若干个顶点到 aabbaabb 邻接 cc。假设此时 dw=Inifinitydw = Inifinityda=5da = 5db=10db = 10

aa 先于 bb 成为当前顶点,由于 da+(a,w)=15<Infinityda + |(a, w)| = 15 < Infinity,故 aa 松弛 dwdw,并将 ww 的前驱置为 aaw.pre=aw.pre = a

bb 成为当前顶点时,由于 db+(b,w)=11<15db + |(b, w)| = 11 < 15,故 bb 松弛 dwdw,并将 ww 的前驱置为 bbw.pre=bw.pre = b

一个直观的观察是,来自 ww 入边的松弛,使得 dwdw 不断更新至所有可能的路径距离的最小值。以下严格证明该算法正确性。


算法正确性证明

利用数学归纳法(结合反证法)证明。

本证明参考了这个帖子

a) 数学归纳法的证明过程

  1. 起始验证。对于命题 P(n)P(n),当 n=1n = 1 时命题 PP 成立。

  2. 假设命题成立。假设命题 P(n)P(n)n=m(m>1,mN)n = m (m > 1, m ∈ N) 时成立。

  3. 递推证明。根据2的假设,若能证明 n=m+1n = m + 1 时命题 PP 成立,则命题得证。

例如,有命题 P1+2+3...+n=n(n+1)/2P:1+2+3...+n = n*(n+1)/2,按照数学归纳法证明如下:

  1. 起始验证。当 nn 等于 11 时,1=1(1+1)/21 = 1*(1+1)/2,命题成立。

  2. 假设命题成立。假设命题等于 mm 时成立,1+2+3+...+m=m(m+1)/21+2+3+...+m = m*(m+1)/2

  3. 递推证明。根据2的假设,如果能证明 n=m+1n = m+1 时命题正确,则命题 PP 成立。

    证明:在2所示式子左右两边加上 m+1m+1,得到 1+2+3+...+m+(m+1)=m(m+1)/2+(m+1)1+2+3+...+m+(m+1) = m*(m+1)/2 + (m+1)

    等号右边可以写成 (m+1)(m+2)/2(m+1)*(m+2)/2,显然该形式就是将 n=m+1n = m+1代入原命题 PP 的形式,证毕。


b) 证明Dijkstra算法的正确性

命题 PP:Dijkstra算法第 nn 次进入while时,会将第 nn 个顶点加入距离已确定顶点集合 AA 中,此时对于顶点 vA(∀v ∈ A(nn 个),总有 dv=δvdv = δv

dvdv 表示由Dijkstra算法得到的最短距离估计,对于源点 ss ,在程序开始时赋予 ds=0ds = 0,对于其他顶点,由松弛操作得到。δvδv 表示实际的顶点 vv 到源点的最短距离。

  1. 起始验证。当 nn 等于 11 时,AA 集合中只有源点 ss 自身,ds=0ds = 0 (程序开始时赋值得到),且知 δv=0δv = 0,故 n=1n=1 时命题正确。

  2. 假设命题成立。假设命题 PPnn 等于 mm 时,P(m)P(m) 成立,即算法经过 mm 次while,得到具有 mm 个顶点的集合 AA,对于顶点 vA∀v ∈ A (共 mm 个),总有 dv=δvdv = δv

  3. P(m+1)P(m+1) 递推证明。根据 2 的假设,如果能证明第 m+1m+1 个顶点 uu 被放入集合 AA 时有 du=δudu = δu,则命题 PP 成立。

    更详细地,A=m|A| = m 时,在点集 B(B=SA)B (B = S - A) 中根据算法规则找到距离最短的顶点 uu,将该顶点将作为第 m+1m+1 个顶点放入A中,放入后 A=m+1|A| = m + 1,如果能证明 du=δudu = δu,使得 P(m+1)P(m+1) 成立,则对于顶点 vA∀v ∈ A (共 m+1m+1 个),有 dv=δvdv = δv

    以反证法证明之。


    3.1 🤔 假设 m+1m+1du=δudu = δu 不成立,即有如下式(1), 之后的目标是 根据已知条件导出某种矛盾情形,推翻该假设。

    (1) δu<duδu < du

    δuδu 是实际的 uu 到源点的最短距离,du=δudu = δu 不成立时只能是 δu<duδu < du。算法保证了从 ssuu 的过程一定是一条由图中的有向边构成的 连续路径,只要是连续路径,无论有多少条这样的路径,一定有一条最短路径,其长度记作 δuδu。并使得其他路径长度必大于等于 δuδu。


    3.2 🤔 根据3.1的假设,存在一条从源点 ssuu 的路径 PuPu,该路径是 ssuu 的最短路径,即 Pu=δu<du|Pu| = δu < du。路径 PuPu 一定有不在 AA 集内的顶点 (至少有 uu 不在 AA 集中,注意此时 uu 还未被选入 AA 中),同时也有在 AA 集中的点 (至少有 ss 点在 AA 集中),可以假设 PuPu 经过 xxyy,其中 xx 在A中 (可以是 ss),yy 在B中 (可以是 uu 本身),yyuu 的过程中也可以再进入 AA,如下图。PxPxPuPu 在顶点 xx 结束的子路径,因为路径 Px+(x,y)Px + (x, y) 为路径 PuPu 的一部分,所以有:

    (2) Px+(x,y)Pu=δu|Px| + |(x, y)| ≤ |Pu| = δu

    这是显然的,因为 Px+(x,y)Px + (x, y)PuPu 的一部分,当 y=uy=u 时取到等号。



    3.3 🤔 在 xx 之前被选中进入 AA 集内时,对其邻接顶点 yy 执行过 松弛操作,该操作会比较 dx+(x,y)dx + |(x, y)| 是否小于 dydy,若小于则以 dx+(x,y)dx + |(x, y)| 更新 dydy 的值,所以如果更新了,更新之后有 dy=dx+(x,y)dy = dx + |(x, y)| ,如果没更新,说明 dy<dx+(x,y)dy < dx + |(x, y)|。假设之后 yy 还会被 yy 的其他前驱顶点更新 dydy 值 (当该前驱顶点进入 AA 集时),那 dydy 只会变得更小,所以一定有:

    (3) dydx+(x,y)dy ≤ dx + |(x, y)|

    比较式 (2) 和式 (3) 中的 Px|Px|dxdx,因为 dx=δxdx = δx (由步骤2的 P(m)P(m) 假设给出,顶点 xxP(m)P(m) 假设的 mm 个顶点之一),而 PxPx 只是若干从 ssxx 的路径之一,因此必有 d(x)Pxd(x) ≤ |Px|,当 PxPx 恰是 ssxx 的最短路径时取到等号。所以根据式 (2) 和式 (3) 有:

    dydx+(x,y)Px+(x,y)Pudy ≤ dx + |(x, y)| ≤ |Px| + |(x, y)| ≤ |Pu|,即

    (4) dyPu=δudy ≤ |Pu| = δu

    3.4 🤔 顶点 yyuu 均在 BB 集中,根据算法规则,uu 作为第 m+1m+1 个定点被放入 AA 集中时,其在 BB 集中相比于BB 集中的其他顶点(自然也包括 yy ),到源点 ss 的距离最小,显然有:

    (5) dudydu ≤ dy

    结合式 (1),式 (4),式 (5)得到:

    (6) δu<dudyPu=δuδu < du ≤ dy ≤ |Pu| = δu ,即 δu<δuδu < δu

至此,由3.1的假设 「d(u)=δ(u)d(u) = δ(u) 不成立」导出了 矛盾,所以 d(u)=δ(u)d(u) = δ(u) 是成立的,Dijkstra算法正确性得证。