Dijkstra算法實際上是一個貪婪算法(Greedy algorithm)。因為該算法總是試圖優先訪問每一步循環中距離起始點最近的下一個結點。Dijkstra算法的過程如下圖所示。
初始化
- 給定圖中的一個結點
s
作為起始點。 - 給定一個數組
dist[]
存儲圖中所有結點到s
的距離。將dist[s]
初始化為0
。對於圖中的其他結點v
,初始化dist[v]
為無窮大。初始化為無窮大的意義在於我們假設其余所有結點在當前情況下尚未與s
聯通。隨着算法的執行,dist[v]
會保存圖中從s
到v
的最短路徑的距離。 - 給定一個minimum Heap,記為
Q
。堆頂為當前情況下距離s
最近的結點及相應的距離。將(s, 0)
放入堆中。 - 給定一個Set,記為
S
,保存所有已經訪問過的結點。Set初始為空。基於Dijkstra算法的性質,我們總是以最短的路徑遍歷每一個結點,因此對於任一結點,一旦我們已經訪問過,就代表着我們已經得到了從s
到達這一結點的最短路徑。
計算最短路徑
- 當
Q
不為空的情況下,取出堆頂的元素(v, [dist[v])
—— 也就是當前距離s
最近的結點v
,及其距離dist[v]
。 - 如果
v
在S
中,則代表我們已經訪問過v
的最短路徑。那么跳過當前v
,重復步驟1。 - 否則,將
v
放在S
中。 - 對於每一個與
v
相鄰的結點t
:
- 如果
dist[v] + weight(v, t) < dist[t]
,則更新dist[t] = dist[v] + weight(v, t)
。同時將(t, dist[t])
放進Q
中。 - 否則,不做任何處理。
- 如果
當算法結束后,dist[]
中保存圖中每一個除s
之外的結點到s
的最短路徑的權重值(或長度)。如果從s
到v
不存在聯通的路徑,則dist[v] = ∞
。
證明算法正確性
假設對於每個已經訪問過的結點v
,dist[v]
存儲從起始點s
到v
的最短路徑。
當算法初始化時,dist[]
中只包含dist[s] = 0
,其正確性顯而易見。
對於其余n-1
個結點,假設u
已經被訪問且v
尚未被訪問,同時u
和v
之間存在一條邊u -> v
,其權重為weight(u,v)
,那么一定有dist[v] = dist[u] + weight(u, v)
。否則的話,假設存在另一條更短的路徑dist[t]
滿足dist[t] + weight(t, v)
,則根據上述算法,t
一定先於u
被訪問,則與我們當前的假設產生了矛盾。該論斷對於余下的所有結點都成立。
因此Dijkstra算法一定能給出從出發點到其余所有結點(在可以到達的情況下)的最短路徑。
復雜度分析
設圖中總計有E
條邊,N
個結點。
時間復雜度:O(ElogE)
。因為所使用的最小堆最大可達O(E)
大小,同時我們從其中將每個元素取出來一次。
空間復雜度:O(N+E)
。其中O(N)
為存儲dist
所用空間。O(E)
為存儲圖的鄰接鏈表及最小堆所用空間。