1. 程式人生 > >數據結構 - 單源最短路徑之迪傑斯特拉(Dijkstra)算法詳解(Java)

數據結構 - 單源最短路徑之迪傑斯特拉(Dijkstra)算法詳解(Java)

previous 代碼 map class matrix () count 就是 可能

  給出一個圖,求某個端點(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)