戴克斯特拉算法

戴克斯特拉算法（Dijkstra's algorithm），又稱迪傑斯特拉算法、Dijkstra算法，是由荷蘭計算機科學家艾茲赫爾·戴克斯特拉在1956年發現的算法，並於3年後在期刊上發表。戴克斯特拉算法使用類似廣度優先搜索的方法解決賦權圖Dijkstra1959的單源最短路徑問題。
該算法存在很多變體：戴克斯特拉的原始版本僅適用於找到兩個頂點之間的最短路徑Dijkstra1959，後來更常見的變體固定了一個頂點作為源結點然後找到該頂點到圖中所有其它結點的最短路徑，產生一個最短路徑樹。
該算法解決了圖 上帶權的單源最短路徑問題196–206。具體來說，戴克斯特拉算法設置了一頂點集合，在集合中所有的頂點與源點之間的最終最短路徑權值均已確定。算法反覆選擇最短路徑估計最小的點並將加入中。該算法常用於路由算法或者作為其他圖算法的一個子模塊。舉例來說，如果圖中的頂點表示城市，而邊上的權重表示城市間開車行經的距離，該演算法可以用來找到兩個城市之間的最短路徑。
應當注意，絕大多數的戴克斯特拉算法不能有效處理帶有負權邊的圖。
戴克斯特拉算法在計算機科學的人工智能等領域也被稱為均一開銷搜索，並被認為是最良優先搜索的一個特例。

**描述**
上圖為戴克斯特拉演算法應用示意圖。起點以左下角的紅點，目標是右上角的綠點，中間灰色的倒L型為障礙物。藍色空圈表示"暫定"，用以搜索下一步；已經填充顏色的表示探訪過，圖中顏色以紅到綠，越綠表示離起點越遠。所有節點都被均勻探索。
戴克斯特拉算法通過保留目前為止所找到的每個頂點從到的最短路徑來工作。初始時，原點的路徑權重被賦為 0 （即原點的實際最短路徑=0）。同時把所有其他頂點的路徑長度設為無窮大，即表示我們不知道任何通向這些頂點的路徑。當算法結束時， 中儲存的便是從到的最短路徑，或者如果路徑不存在的話是無窮大。
鬆弛操作是戴克斯特拉算法的基礎操作：如果存在一條從到的邊，那麼從到的一條新路徑是將邊添加到從到的路徑尾部來拓展一條從到的路徑。這條路徑的長度是。如果這個值比目前已知的的值要小，那麼可以用這個值來替代當前中的值。鬆弛邊的操作一直執行到所有的都代表從到的最短路徑的長度值。
算法維護兩個頂點集合和。集合保留所有已知實際最短路徑值的頂點，而集合則保留其他所有頂點。集合初始狀態為空，而後每一步都有一個頂點從移動到。這個被選擇的頂點是中擁有最小的值的頂點。當一個頂點從中轉移到了中，算法對的每條外接邊進行鬆弛。
《算法導論》中給出了以下偽代碼：該偽代碼計算並保留圖中原點到每一頂點的最短距離。其中，函數將頂點集合中有最小值的頂點從中刪除並返回。
-{}-
1  function Dijkstra(G, w, s)
2   INITIALIZE-SINGLE-SOURCE(G, s)                //實際上的操作是將每個除原點外的頂點的置為無窮大，
3   
4                                   //是頂點的一個優先隊列，以頂點的最短路徑估計排序
5   while()
6       do           //選取為中最短路徑估計最小的頂點
7       
8       for each vertex v 
9            do RELAX(u, v, w)            //鬆弛成功的結點會被加入到隊列中
如果我們只對在和之間尋找一條最短路徑的話，我們可以在第5或第6行添加條件如果滿足的話終止程序。
在肯尼·羅森所著的《離散數學及其應用》中給出瞭如下的另一份偽代碼：
-{}-
1 procedure Dijkstra(G：邊全為正權的圖）
2   {G帶有頂點和若干邊}
3    for  to n
4       
5    
6    
7    while 
8    begin
9          不屬於的最小的一個頂點
10        
11        for 所有不屬於的頂點
12            if  then 
13    end{從a到z的最短路長度}
在該  算法演示頁面 date=20240829120512 ，可以自定義節點，邊的權重等信息，然後觀察算法每一步的執行過程。

**時間複雜度**
我們可以用大O符號將該算法的運行時間表示為邊數和頂點數的函數。
對於任何基於頂點集的實現，算法的運行時間是，其中和分別表示完成鍵的降序排列時間和從中提取最小鍵值的時間。
對於沒有任何優化的戴克斯特拉算法，實際上等價於每次遍歷了整個圖的所有結點來找到Q中滿足條件的元素（即尋找最小的頂點是的），此外實際上還需要遍歷所有的邊一遍，因此算法的複雜度是。
對於邊數少於的稀疏圖來說，可以用鄰接表來更有效的實現該算法。
可以使用一個二叉堆或者斐波納契堆用作優先隊列來尋找最小的頂點（）以優化算法。當用到二叉堆的時候，算法所需的時間為，斐波納契堆能提高一些性能，讓算法運行時間達到。然而，使用斐波納契堆進行編程，有時會由於算法常數過大而導致速度沒有顯著提高。
下面是一些戴克斯特拉算法經典實現的複雜度比較：

**正確性證明**
艾茲赫爾·戴克斯特拉，戴克斯特拉算法的發現者
戴克斯特拉本人在他的論文中給出了一份簡單的證明。
《算法導論》使用循環不變式（數學歸納法）給出瞭如下的一份證明：
已知一帶權圖，其加權函數的值非負，源點為。對該圖運行戴克斯特拉算法，對所有有。其中表示u點的最短路徑估計，表示到點的最短路徑。
證明：證明如下的循環不變式成立即可：在每次執行EXTRACT-MIN時，對每個頂點，有成立即可。由於上界性質，在加入了之後，一旦有，則在後面的每次循環中都不會改變這個性質。
初始化：第一次循環前，，因此循環不變式顯然成立。
保持：實際上要證明每一輪循環中加入到中的結點滿足。利用反證法，假設是第一個不滿足此條件的結點，考慮循環開始前的狀況，首先一定不等於，這是顯然的。其次一定有到的路徑，否則路徑為無窮大。那麼假設在進入時，有最短路徑，假設該路徑上存在兩個點，。、，且x是y的前驅，路徑可以分解為（此處表示經過這條路徑，後同），其中路徑和路徑可以為空。由於是第一個不滿足的，又因為是滿足該條件的，而且一定已經被鬆弛過了，所以是滿足該條件的。
現在只需要推出矛盾，即可證明u不存在：在之前出現，而且圖中所有權值非負，因此有，所以：，但是由於和同時在中，因此，因此必有，也就證明了點不可能不滿足該條件，上述假設為假，原命題得證。
終止：終止時，，由於，因此，因此對所有有。

**起源與歷史**
戴克斯特拉1956年在荷蘭數學和計算機科學研究學會擔任程序員時為了展示新型計算機ARMAC的功能曾思考過最短路徑問題的解法。他的目標是讓不去實際計算的人也能理解這個問題和解決的方法，於是他在發現了這個算法之後在ARMAC上做了簡單實驗。1959年，他正式將此算法發表在期刊上，該算法也成為了戴克斯特拉成名的基石之一。

**相關應用**
一個多區域OSPF網絡，在OSPF中使用本算法計算最短路徑
中需要計算最短路時常常要用到該算法，該算法在開放最短路徑優先和中間系統到中間系統協議中的相關應用是其在網絡路由中的典型實現。
戴克斯特拉算法及其改進算法應用廣泛，尤其是在尋路、交通、規劃中。
如果有已知信息可用來估計某一點到目標點的距離，則可改用A*搜尋算法，以減小最短路徑的搜索範圍，戴克斯特拉算法本身也可以看作是A*搜索算法的一個特例。
戴克斯特拉算法本身採用了與Prim算法類似的貪心策略。快速行進算法與戴克斯特拉算法同樣有相似之處。

**參考源程序 ==                                                                                                                                   
以下是該算法使用堆優化的一個C++實現參考：
// 無向圖
#include 
using namespace std;
#define INF 0x3f3f3f3f
// iPair ==> Integer Pair（整數對）
typedef pair iPair;
// 加邊
void addEdge(vector> adj, int u, int v, int wt)
{
adju.push_back(make_pair(v, wt));
adjv.push_back(make_pair(u, wt));
}
// 計算最短路
void shortestPath(vector> adj, int V, int src)
{
// 關於stl中的優先隊列如何實現，參考下方網址：
// http://geeksquiz.com/implement-min-heap-using-stl/
priority_queue, greater> pq;
// 距離置為正無窮大
vector dist(V, INF);
vector visited(V, false);
// 插入源點，距離為0
pq.push(make_pair(0, src));
distsrc = 0;
/* 循環直到優先隊列為空 */
while (!pq.empty())
{
// 每次從優先隊列中取出頂點事實上是這一輪最短路徑權值確定的點
int u = pq.top().second;
pq.pop();
if (visitedu)
{
continue;
}
visitedu = true;
// 遍歷所有邊
for (auto x : adju)
{
// 得到頂點邊號以及邊權
int v = x.first;
int weight = x.second;
// 可以鬆弛
if (distv > distu + weight)
{
// 鬆弛
distv = distu + weight;
pq.push(make_pair(distv, v));
}
}
}
// 打印最短路
printf("Vertex Distance from Source\n");
for (int i = 0; i  adjV;
addEdge(adj, 0, 1, 4);
addEdge(adj, 0, 7, 8);
addEdge(adj, 1, 2, 8);
addEdge(adj, 1, 7, 11);
addEdge(adj, 2, 3, 7);
addEdge(adj, 2, 8, 2);
addEdge(adj, 2, 5, 4);
addEdge(adj, 3, 4, 9);
addEdge(adj, 3, 5, 14);
addEdge(adj, 4, 5, 10);
addEdge(adj, 5, 6, 2);
addEdge(adj, 6, 7, 1);
addEdge(adj, 6, 8, 6);
addEdge(adj, 7, 8, 7);
shortestPath(adj, V, 0);
return 0;
}
以下是該算法Python的一個實現：
import sys
max = sys.maxsize
vertices_number = 6
adjacency_matrix = 
0, 1, 10, -1, -1, 2,
10, 0, 1, -1, -1, -1,
1, 10, 0, -1, -1, -1,
-1, -1, 2, 0, 1, 10,
-1, -1, -1, 10, 0, 1,
-1, -1, -1, 1, 10, 0
start = 
dest = "2", "5"
key = 
def init_keys(s: int):
global key
key =  max  * vertices_number
keys = 0
def dijkstra(from_vertex, dest_vertex):
fid = int(from_vertex) - 1
tid = int(dest_vertex) - 1
init_keys(fid)
rel = fid
min_vertex = fid
hop_path = {}
while len(rel)  0 \
and keyi > keymin_vertex + adjacency_matrixmin_vertexi:
keyi = keymin_vertex + adjacency_matrixmin_vertexi
hop_path.update({i + 1: {"from": min_vertex + 1, "cost": adjacency_matrixmin_vertexi}})
if min_vertex not in rel:
rel.append(min_vertex)
min_vertex = tid
for i in range(vertices_number):
if i not in rel and keyi  {}".format(str(next_hop), cost ,path_str)
path_str =  "{} -({})-> {}".format(str(hop_pathnext_hop"from"), hop_pathnext_hop"cost", path_str)
return keytid, path_str
def find_shortest_router():
for s in start:
print("Forwarding Table for {}".format(s))
print("{:>10} {:>10}       {}".format("To", "Cost", "Path"))
for d in dest:
c, n = dijkstra(s, d)
print("{:>10} {:>10}       {}".format(d, c, n))
def main():
for i in range(1, vertices_number + 1):
if str(i) not in dest:
start.append(str(i))
find_shortest_router()
if __name__ == '__main__':
main()

**參見**
 圖論- A*搜尋演算法- 貝爾曼-福特算法- 寬度優先搜索- Flood fill- Floyd-Warshall算法- 最長路徑問題

**參考**

**參考文獻**

**擴展閱讀**
 cite book  
title = Introduction to Algorithms | edition = second | publisher = MIT Press、S&P Global | year = 2001 | isbn = 0-262-03293-7 | chapter = Section 24.3: Dijkstra's algorithm | pages = 595–601 | ref = harv-  doi = 10.1287/trsc.32.1.65- doi=10.1016/0020-0190(77)90002-3- ref=harv
*doi=10.1145/261342.261352
*ref=harv
*dead-url=yes
*date=2007-05

**外部連結**
  迪科斯徹算法分解演示視頻（優酷） date=20210213073618 -  Animation of Dijkstra's algorithm date=20140219102217 -  The Boost Graph Library (BGL) date=20081202234015 -  Interactive Implementation of Dijkstra's Algorithm date=20200217212029 -  Shortest Path Problem: Dijkstra's Algorithm-  Dijkstra算法使用TDD的一個實現 date=20201229133730 -  Graphical explanation of Dijkstra's algorithm step-by-step on an example date=20210317123138