dijkstra算法介紹:即迪傑斯特拉算法,是從一個頂點到其余各頂點的最短路徑算法,解決的是有向圖中最短路徑問題。迪傑斯特拉算法主要特點是以起始點為中心向外層層擴展,直到擴展到終點為止,是一種廣度優先的搜索方法。
dijkstra算法原理:最優子路徑存在。假設從S→E存在一條最短路徑SE,且該路徑經過點A,那么可以確定SA子路徑一定是S→A的最短路徑。證明:反證法。如果子路徑SA不是最短的,那么就必然存在一條更短的'SA,從而SE路徑也就不是最短,與原假設矛盾。
dijkstra算法缺點:與此前說過的viterbi不同,此算法能夠求出從起點到其余每個結點的最短路徑,所以需要遍歷所有的路徑和結點,計算復雜度比較大。
dijkstra算法例子:求從結點0到各個結點的最短路徑。
step1:首先建立兩個集合S={}:表示已經找到最短路徑的結點;U={}:表示尚未找到最短路徑的結點。顯然,S與U互為補集,S U U=所有結點組成的集合,當U為空的時候算法結束,所有結點最短路徑均已找到。
step2:建立一個數組dist[i],用於存放起點0到該結點i的最短路徑(可能需要更新,接下來會解釋)。然后再建立一個布爾數組s[i](初值均為0),用於表示該結點是否已經找到最短路徑,如已經找到便不再遍歷。
step3:具體執行部分:
A:初始點設定。對於結點0,首先將其納入到S集合中,然后尋找並計算與結點0直接相連路徑的長度,即dist[1]=100,dist[30]=2,dist[4]=10(這時dist[0]=0)。而不能直接到達的結點距離為無限大∞。這里使用dist[3]=99999,方便程序比較大小。然后使s[0]=1,表示已經遍歷過該結點。
B:選取最小dist[i]。比較dist[1],dist[3],dist[4]的長度,選擇長度最短的dist[4],並將結點4納入到S集合中,令s[4]=1,表明0到4的最短路徑已經找到,且值為10。原因:最優子路徑存在原理。由於dist[1],dist[3]均大於dist[4],所以若選擇走經過結點1、3到達結點4路徑,無論如何也不可能找到一條小於直接從結點0到結點4的路徑!這個結論非常非常非常重要,是理解這個算法的關鍵!后面會反復用到,每一輪循環都要比較並選取最小的dist[i]。
C:更新dist[i]。現在,我們開始以結點4為中心向外擴展(廣度優先)。現在,結點4可以到達結點3了,也表明從結點0可以通過結點4到達結點3了。至於要更新dist[i]的原因如下圖:
在第一次選擇中,我們納入了起點A,然后由於dist[C]=6<dist[B]=20,我們又納入了C點。這時,我們發現我們還可以通過C點到達B點,所以我們必須比較dist[B]與dist[C]+|CB|的大小。這以下這個例子中,顯然6+7=13<20,所以dist[B]需要從20更新成13。所以,我們不難發現:每納入一個新的結點,我們都需要比較由這個結點新擴展出來結點所生成路徑與原來路徑的長度。
step4:重復上述步驟B、C,直到U集合清空,s[i]中所有值均為1。這就表明圖中所有點都找到了最短路徑。
下面放出以上例子的步驟表,如果能理解就表明基本了解dijkstra算法的思想了。

dijkstra算法重點:理解為何需要選取最小的dist[i];理解為何需要更新dist[i]。
1 #include <iostream> 2 using namespace std; 3 4 const int maxnum = 100; 5 const int maxint = 999999; 6 7 8 void Dijkstra(int n, int v, int *dist, int *prev, int c[maxnum][maxnum]) 9 { 10 bool s[maxnum]; // 判斷是否已存入該點到S集合中 11 12 // 確認起始節點並設置相關參數 13 for(int i=1; i<=n; ++i) 14 { 15 dist[i] = c[v][i]; // 將鄰接矩陣中數據傳入dist[]中 16 s[i] = 0; // 初始所有點都未納入S中 17 if(dist[i] == maxint) 18 prev[i] = 0; // 該點暫時沒找到前驅結點 19 else 20 prev[i] = v; // 該點的前驅結點為起始點v 21 } 22 dist[v] = 0; 23 s[v] = 1; 24 25 // 依次將未放入S集合的結點中,取dist[]最小值的結點,放入結合S中 26 // 一旦S包含了所有V中頂點,dist就記錄了從源點到所有其他頂點之間的最短路徑長度 27 for(int i=2; i<=n; ++i) 28 { 29 int tmp = maxint; 30 int u = v; 31 32 // 找出當前未使用的點j的dist[j]最小值 33 for(int j=1; j<=n; ++j) 34 if((!s[j]) && dist[j]<tmp) 35 { 36 u = j; // u保存當前鄰接點中距離最小的點的號碼 37 tmp = dist[j]; 38 } 39 s[u] = 1; // 表示u點已存入S集合中 40 41 // 更新dist,用更小的值代替原先的值 42 for(int j=1; j<=n; ++j) 43 if((!s[j]) && c[u][j]<maxint) 44 { 45 int newdist = dist[u] + c[u][j]; 46 if(newdist < dist[j]) 47 { 48 dist[j] = newdist; 49 prev[j] = u; 50 } 51 } 52 } 53 } 54 55 // 記錄下這條最短路徑的路線(經過的結點) 56 void searchPath(int *prev,int v, int u) 57 { 58 int que[maxnum]; 59 int tot = 1; 60 que[tot] = u; 61 tot++; 62 int tmp = prev[u]; 63 while(tmp != v) 64 { 65 que[tot] = tmp; 66 tot++; 67 tmp = prev[tmp]; 68 } 69 que[tot] = v; 70 for(int i=tot; i>=1; --i) 71 if(i != 1) 72 cout << que[i] << " -> "; 73 else 74 cout << que[i] << endl; 75 } 76 77 int main() 78 { 79 // 各數組都從下標1開始 80 int dist[maxnum]; // 表示當前點到源點的最短路徑長度 81 int prev[maxnum]; // 記錄當前點的前一個結點 82 int c[maxnum][maxnum]; // 記錄圖的兩點間路徑長度 83 int n, line; // 圖的結點數和路徑數 84 85 // 輸入結點數 86 cin >> n; 87 // 輸入路徑數 88 cin >> line; 89 int p, q, len; // 輸入p, q兩點及其路徑長度 90 91 // 初始化c[][]為maxint 92 for(int i=1; i<=n; ++i) 93 for(int j=1; j<=n; ++j) 94 c[i][j] = maxint; 95 96 for(int i=1; i<=line; ++i) 97 { 98 cin >> p >> q >> len; 99 if(len < c[p][q]) // 有重邊 100 { 101 c[p][q] = len; // p指向q 102 c[q][p] = len; // q指向p,這樣表示無向圖 103 } 104 } 105 106 for(int i=1; i<=n; ++i) 107 dist[i] = maxint; 108 for(int i=1; i<=n; ++i) 109 { 110 for(int j=1; j<=n; ++j) 111 printf("%8d", c[i][j]); 112 printf("\n"); 113 } 114 115 // 假定起點為結點1 116 Dijkstra(n, 1, dist, prev, c); 117 118 // 最短路徑長度 119 cout << "源點到最后一個頂點的最短路徑長度: " << dist[n] << endl; 120 121 // 路徑 122 cout << "源點到最后一個頂點的路徑為: "; 123 124 // 假定起點為結點1 125 searchPath(prev, 1, n); 126 }
作者:俊爺拒做學渣
鏈接:https://www.jianshu.com/p/c9b27617502e
