Dijkstar算法是荷兰数学家迪克斯屈拉(or迪杰斯特拉?)在1959年发现的一个算法。是现有的几个求带权图中两个顶点之间最短通路的算法之一。算是一个相当经典的算法了。
迪克斯屈拉算法应用于无向连通简单带权图中,求出顶点a 与z 之间的最短通路的长度。我感觉其算法精髓就是:找到第一个与a 最靠近的顶点,然后找第二个,续行此法,直到找到的顶点是z 为止。该算法依赖于一系列的迭代。通过在每次迭代中添加一个顶点来构造出特殊顶点的集合。在每次迭代中完成一个标记过程。在这个标记过程中,用只包含特殊顶点的从a 到w 的最短通路的长度来标记w。添加到特殊顶点集合中的顶点是在还没有成为特殊顶点的那些顶点中带有最小标记的那个顶点。
算法细节是:首先用0 来标记a 而用∞ 标记其余的顶点。用记号L0(a) = 0 和 L0(v) = ∞ 表示在没有发生任何迭代之前的这些标记(下标0表示第零次迭代)。这些标记是从a 到这些顶点的最短通路的长度。迪克斯屈拉算法是通过形成特殊顶点的集合来进行的。设Sk 表示在标记过程中k 次迭代之后的特殊顶点的集合。首先令S0 = Ø。集合Sk 是通过把不属于Sk-1 的带最小标记的顶点u 添加到Sk-1 里来形成的。一旦把u 添加到Sk 中,就更新所有不属于Sk 的顶点的标记,使得顶点v 在第k 个阶段的标记Lk(v) 是只包含Sk 中顶点(即已有的特殊顶点的集合再加上u )的从a 到v 的最短通路的长度。设v 是不属于SK 的一个顶点。为了更新v 的标记,注意Lk(v) 是只包含Sk 中顶点的从a 到v 的最短通路的长度。当利用下面这个观察结果时,就可以有效地完成这个更新:从a 到v 的只包含Sk 中顶点的最短通路,或者是从a 到v 的只包含Sk-1 中顶点(即不包括u 在内的特殊顶点)的最短通路,或者是在k-1 阶段从a 到u 的最短通路上加上边(u, v)。简言之:Lk(a, v) = min{Lk-1(a, v), Lk-1(a, u) + w(u, v)}。这个过程这样迭代:相继地添加顶点到特殊顶点集合里, 直到添加到z 为止。当把z 添加到特殊顶点集合里时,它的标记就是从a 到z 的最短通路的长度。
下面,我们用数学归纳法来证明这个算法的正确性。
基础步骤:在第零次迭代,即k = 0 时,S = {a},所以从a 到除a 外的顶点的最短通路的长度是∞,而从a 到a 本身的最短长度为0(这里允许一个通路不包含任何边在内)。因此基本情况是正确的。
归纳步骤:用下列断言做归纳假设:在第k 次迭代中
- 在S 中的顶点v (v ≠ 0)的标记是从a 到这个顶点的最短通路的长度。
- 不在S中的顶点的标记是(除了这个顶点自身之外)只包含S 中顶点的从a 到这个顶点的最短通路的长度。
假定归纳假设对k 次迭代成立。令v 是在第k+1 次迭代中添加到S 中的顶点,使得v 是在第k 次迭代结束时带最小标记的不在S 中的顶点(如果该顶点不唯一,可以采用带最小标记的任意顶点)。根据归纳假设,可以看出在第k+1 次迭代之前,S 中的顶点都用从a 出发的最短通路的长度来标记。另外,必须用从a 到v 的最短通路的长度来标记v。假如情况不是这样,那么在第k 次迭代结束时,就可能存在包含不在S 中的顶点的长度小于Lk(v) 的通路(因为Lk(v) 是在第k 次迭代之后,只包含S 中顶点的从a 到v 的最短通路的长度)。设u 是在这样的通路里不属于S 的第一个顶点。则存在一条从a 到u 的只包含S 中顶点的长度小于Lk(v) 的通路。这与对v 的选择矛盾。因此,在第k+1 次迭代结束时1 成立。设u 是在第k+1 次迭代之后不属于S 的一个顶点。从a 到u 的只包含S 里顶点的最短通路要么包含v,要么不包含v。若它不包含v,根据归纳假设,它的长度是Lk(u)。若它确实包含v,则它必然是这样组成的:一条边从a 到v 的具有最短可能长度的通路,其中包含S 中不同于v 的元素,后面接着从v 到u 的边。在这种情况中它的长度是Lk(v) + w(v, u)。这样就证明了2 成立,因为Lk+1(u) = min{Lk(u), Lk(v)+w(v, u)}。
根据数学归纳法原理,我们证明了迪克斯屈拉算法的正确性。
学习算法的过程中,我们常常考察算法的时间复杂度并依此衡量算法的优劣。因此一个很自然的问题就是:迪克斯屈拉算法的时间复杂度是多少?
因为有n 个顶点,所以算法最多使用n-1 次迭代,因为每次迭代添加一个顶点到特殊顶点集合里。而在每个迭代的过程中,可以用不超过n-1 次比较来找出不在S 中的带最小标记的顶点。而每次迭代里要更新的标记也不超过n-1 个。因此每次迭代使用不超过2(n-1) 次运算,粗略估计上界的话,可以得到O(n^2) 是其最坏时间复杂度。严格点的话,可以算一下:
∑(i~1,n)n-i + ∑(i~1,n)2(n-i-1) = (3n^2)/2 - 9n/2。
(完)