數據結構 - 單源最短路徑之迪傑斯特拉(Dijkstra)算法詳解(Java)
給出一個圖,求某個端點(goal)到其余端點或者某個端點的最短路徑,最容易想到的求法是利用DFS,假設求起點到某個端點走過的平均路徑為n條,每個端點的平均鄰接端點為m,那求出這個最短路徑使用DFS算法的時間復雜度為O(mn), 這顯然是十分不理想的。
於是提出迪傑斯特拉(Dijkstra)算法,為解決有向圖中某個端點到其余端點的最短路徑, 在端點數為m,時間復雜度為O(m2)的情況下求出結果,前提條件是每條邊的權重不能使負值。
接下來講述算法細節:
1、把所有端點劃分為兩個集合,所有已找到最短路徑的端點集合和未找到最短路徑的端點集合(初始只有起點在已找到最短路線端點集合中)。
2、維護一個長度為m的距離數組
3、循環m-1次,每次從距離數組中找到最小的數值,該數值即為該端點的最短路徑(比如起點為A,遍歷的時候找到最小數值是B,那麽這個值就是B點的最短距離,因為不可能從A出去的其他路徑再經過其他路徑再到達B的距離比這個距離更小,除非有路徑的權重的負值,但這與Dijkstra的前提條件相悖)。
4、對當前找到最短路徑的端點,如果從該點出發,所能到達的未找到最短路徑的端點小於當前距離數組裏面的值,則更新這個值。
5、循環m-1次找到所有端點的最短路徑,或者在某個循環未能找到符合要求的端點,則說明剩下的端點不可到達,都結束此次計算。
過程被我表達有點晦澀...下面可以通過代碼理解。
1 // calculate the shortest distance from start point to end point 2 public void calcDistance(int start, int[][] matrix) { 3 this.matrix = matrix; 4 this.start = start; 5 int cnt = matrix.length; //Point count 6 this.goalCnt = cnt; 7 boolean[] found = new boolean[cnt]; //true value indicate the shortest distance from start to n have been found 8 int[] dis = new int[cnt]; // store the shortest distance from start to n, initial as INT_MAX 9 int[] preRoute = new int[cnt]; //preRoute[u] = v represent that the previous goal of shortest path to u is v 10 for (int i = 0; i < cnt; i++) { 11 dis[i] = start == i ? 0 : matrix[start][i] != 0 ? matrix[start][i] : Integer.MAX_VALUE; 12 preRoute[i] = -1; 13 } 14 found[start] = true; 15 // find out one shortest path in one iteration 16 for (int i = 0; i < cnt - 1; i++) { 17 // the shortest distance of the index of goal was found in current iteration 18 int tDis = Integer.MAX_VALUE, 19 tInd = -1; 20 for (int j = 0; j < cnt; j++) { 21 if (!found[j] && dis[j] < tDis) { 22 tInd = j; 23 tDis = dis[j]; 24 } 25 } 26 if (-1 == tInd) { 27 //The left goal is unreachable 28 break; 29 } 30 found[tInd] = true; 31 distance.put(tInd, dis[tInd]); 32 // find out the route of the tInd goal 33 int e = tInd; 34 Stack<Integer> stack = new Stack<>(); 35 while (-1 != e) { 36 stack.push(e); 37 e = preRoute[e]; 38 } 39 route.put(tInd, stack); 40 //update the shorter path 41 for (int j = 0; j < cnt; j ++) { 42 if (matrix[tInd][j] != 0) { 43 int newDis = dis[tInd] + matrix[tInd][j]; 44 if (!found[j] && newDis < dis[j]) { 45 dis[j] = newDis; 46 preRoute[j] = tInd; // tInd is the previous goal of the shortest path to j goal 47 } 48 } 49 } 50 } 51 }
以上為Dijkstra的核心算法,傳入一個圖一個起點,求出這個起點到其余端點的最短路徑,found[n]為true表示下標為n的端點的最短路徑已經找到,dis數組即為上文所提距離數組,32~39行以及preRoute數組只是為了找出路徑所經節點,不屬於算法內容。
下面我們找一張圖檢驗一下算法結果,S(N0)為起點。
化為矩陣形式如下:
public static void main(String[] args) { // test data int[][] matrix = { {0,3,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, {3,0,1,0,1,0,0,0,0,4,0,0,0,0,0,0,0,0}, {1,1,0,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0}, {1,0,1,0,0,2,2,1,0,0,0,0,0,0,0,0,0,0}, {0,1,2,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0}, {0,0,1,2,1,0,1,0,0,3,1,0,3,0,0,0,0,0}, {0,0,0,2,0,1,0,1,2,0,0,0,2,4,3,0,0,0}, {0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,2,1,0,0,0,0,0,0,1,3,0,0}, {0,4,0,0,1,3,0,0,0,0,1,1,0,0,0,0,0,0}, {0,0,0,0,0,1,0,0,0,1,0,1,2,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,1,0}, {0,0,0,0,0,3,2,0,0,0,2,1,0,2,0,0,1,0}, {0,0,0,0,0,0,4,0,0,0,0,0,2,0,1,2,2,1}, {0,0,0,0,0,0,3,0,1,0,0,0,0,1,0,1,0,0}, {0,0,0,0,0,0,0,0,3,0,0,0,0,2,1,0,0,4}, {0,0,0,0,0,0,0,0,0,0,0,1,1,2,0,0,0,1}, {0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,4,1,0}}; Dijkstra dijkstra = new Dijkstra(); dijkstra.calcDistance(0, matrix); for (int i = 1; i < dijkstra.getGoalCnt(); i++) { System.out.println("Destination: " + i); System.out.println("Distance: " + dijkstra.getDistance(i)); System.out.println("Path: " + dijkstra.getRouteMap(i)); System.out.println(); } }
運算結果:
Destination: 1 Distance: 2 Path: 0->2->1 Destination: 2 Distance: 1 Path: 0->2 Destination: 3 Distance: 1 Path: 0->3 Destination: 4 Distance: 3 Path: 0->2->4 Destination: 5 Distance: 2 Path: 0->2->5 Destination: 6 Distance: 3 Path: 0->3->6 Destination: 7 Distance: 2 Path: 0->3->7 Destination: 8 Distance: 3 Path: 0->3->7->8 Destination: 9 Distance: 4 Path: 0->2->4->9 Destination: 10 Distance: 3 Path: 0->2->5->10 Destination: 11 Distance: 4 Path: 0->2->5->10->11 Destination: 12 Distance: 5 Path: 0->2->5->12 Destination: 13 Distance: 5 Path: 0->3->7->8->14->13 Destination: 14 Distance: 4 Path: 0->3->7->8->14 Destination: 15 Distance: 5 Path: 0->3->7->8->14->15 Destination: 16 Distance: 5 Path: 0->2->5->10->11->16 Destination: 17 Distance: 6 Path: 0->3->7->8->14->13->17
初步檢驗結果是正確的。
至此,以上為個人對Dijkstra算法的理解,如有不妥之處,歡迎指出斧正。
尊重知識產權,轉載引用請通知作者並註明出處!
數據結構 - 單源最短路徑之迪傑斯特拉(Dijkstra)算法詳解(Java)