深入理解 Dijkstra 演算法實現原理
阿新 • • 發佈:2018-12-20
迪傑斯特拉(Dijkstra)演算法
- 1典型最短路徑演算法,用於計算一個節點到其他節點的最短路徑。
- 2特點是以起始點為中心向外層層擴充套件(廣度優先搜尋思想),直到擴充套件到終點為止。
大概就是這樣一個有權圖,Dijkstra演算法可以計算任意節點到其他節點的最短路徑
演算法思路
- 初始時,S只包含起點s;U包含除s外的其他頂點,且U中頂點的距離為”起點s到該頂點的距離”[例如,U中頂點v的距離為(s,v)的長度,然後s和v不相鄰,則v的距離為∞]。
- 從U中選出”距離最短的頂點k”,並將頂點k加入到S中;同時,從U中移除頂點k。
- 更新U中各個頂點到起點s的距離。之所以更新U中頂點的距離,是由於上一步中確定了k是求出最短路徑的頂點,從而可以利用k來更新其它頂點的距離;例如,(s,v)的距離可能大於(s,k)+(k,v)的距離。
- 重複步驟(2)和(3),直到遍歷完所有頂點。
演算法圖解
1.選定A節點並初始化
2.執行上述2,3步驟,找出U集合中路徑最短的節點D 加入S集合,從U中移除節點D,更新U中各個頂點到起點A的距離,根據(AD距離 + D到B,C,E的距離< A到B,C,E 的距離)
來更新U集合
3.這時候 B, C
都為3,沒關係。其實這時候他倆都是最短距離,如果從演算法邏輯來講的話,會先取到B點。 執行步驟2,3
思路就是這樣,往後就是大同小異
程式碼實現
package Test; import java.util.ArrayList; import java.util.List; public class Dijkstra { public static final int M = 10000; // 代表正無窮 public static void main(String[] args) { // 二維陣列每一行分別是 A、B、C、D、E 各點到其餘點的距離, // A -> A 距離為0, 常量M 為正無窮 int[][] weight1 = {{0,4,M,2,M}, //A節點到A、B、C、D、E 各點到其餘點的距離 {4,0,4,1,M}, //B節點到A、B、C、D、E 各點到其餘點的距離 {M,4,0,1,3}, //C節點到A、B、C、D、E 各點到其餘點的距離 {2,1,1,0,7}, //D節點到A、B、C、D、E 各點到其餘點的距離 {M,M,3,7,0} //E節點到A、B、C、D、E 各點到其餘點的距離 }; //測試A節點到其他節點的距離 int start = 0; dijkstra(weight1, start); } /** * * 描述:將數字轉換為對應的節點(A,B,C,D,E) * @param * @return */ public static String getNode(int i) { String node=null; switch (i) { case 0: node="A"; break; case 1: node="B"; break; case 2: node="C"; break; case 3: node="D"; break; case 4: node="E"; break; default: break; } return node; } public static void dijkstra(int[][] weight, int start) { // 接受一個有向圖的權重矩陣,和一個起點編號start(從0編號,頂點存在陣列中) // 返回一個int[] 陣列shortPath,表示從start到它的最短路徑長度 int n = weight.length; // 頂點(0,1,2,3,4) //S的作用是記錄已求出最短路徑的頂點集合 int[] s = new int[n]; //U則是記錄還未求出最短路徑的頂點集合 List<Integer> u= new ArrayList<Integer>(); for (int i=0;i<n;i++) { u.add(i); } // 儲存start到其他各點最短路徑的字串表示 String[] path = new String[n]; for (int i = 0; i < n; i++){ path[i] = new String(getNode(start) + "-->" + getNode(i));//預設設定起點到各個頂點的輸出格式 } // 初始化,第一個頂點已經求出 s[start] = 0; //U中排除第一個頂點 u.remove(Integer.valueOf(start)); for (int count = 1; count < n; count++) { // 要加入n-1個頂點 int k = -1; // 選出一個距離初始頂點start最近的未標記頂點 int dmin = Integer.MAX_VALUE; //選擇集合中未標記過的最短路徑的節點(相當於在U中獲取最小路徑的節點) for (int i = 0; i < n; i++) { if (u.contains(i) && weight[start][i] < dmin) { dmin = weight[start][i]; k = i; } } // 將選出的最短路徑節點放到集合S中,並設定該節點的路徑大小 s[k] = dmin; u.remove(Integer.valueOf(k));//排除以放到S集合的節點 // 以k為中間點,修正從start到未訪問各點的距離 for (int i = 0; i < n; i++) {//和所有沒有標記最短路徑進行對比,更新U中的所有最短距離 //如果 '起始點到當前點距離' + '當前點到某點距離' < '起始點到某點距離', 則更新 if (u.contains(i) && weight[start][k] + weight[k][i] < weight[start][i]) { weight[start][i] = weight[start][k] + weight[k][i];//修正起始節點到某點的最短距離 path[i] = path[k] + "-->" + getNode(i);// } } } //輸出 for (int i = 0; i < n; i++) { System.out.println("從" + getNode(start) + "出發到" + getNode(i) + "最短距離為:"+s[i]+",最短路徑為:" + path[i]); } } } 結果 從A出發到A最短距離為:0,最短路徑為:A-->A 從A出發到B最短距離為:3,最短路徑為:A-->D-->B 從A出發到C最短距離為:3,最短路徑為:A-->D-->C 從A出發到D最短距離為:2,最短路徑為:A-->D 從A出發到E最短距離為:6,最短路徑為:A-->D-->C-->E