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

  給出一個圖,求某個端點(goal)到其他端點或者某個端點的最短路徑,最容易想到的求法是利用DFS,假設求起點到某個端點走過的平均路徑爲n條,每一個端點的平均鄰接端點爲m,那求出這個最短路徑使用DFS算法的時間複雜度爲O(mn), 這顯然是十分不理想的。算法

  因而提出迪傑斯特拉(Dijkstra)算法,爲解決有向圖中某個端點到其他端點的最短路徑, 在端點數爲m,時間複雜度爲O(m2)的狀況下求出結果,前提條件是每條邊的權重不能使負值。數組

 

  接下來說述算法細節:this

  一、把全部端點劃分爲兩個集合,全部已找到最短路徑的端點集合未找到最短路徑的端點集合(初始只有起點在已找到最短路線端點集合中)。spa

  二、維護一個長度爲m的距離數組(下面均用這個表達,都是指這個東西),存儲當前全部端點的最短距離。當一個新的端點找到最短路徑的時候,即更新該端點所能到達的其他未找到最短路徑的端點的當前最短距離(初始值是起點到所能到達的端點的距離)。3d

  三、循環m-1次,每次從距離數組中找到最小的數值,該數值即爲該端點的最短路徑(好比起點爲A,遍歷的時候找到最小數值是B,那麼這個值就是B點的最短距離,由於不可能從A出去的其餘路徑再通過其餘路徑再到達B的距離比這個距離更小,除非有路徑的權重是負值,但這與Dijkstra的前提條件相悖)。code

  四、對當前找到最短路徑的端點,若是從該點出發,所能到達的未找到最短路徑的端點小於當前距離數組裏面的值,則更新這個值blog

  五、循環m-1次找到全部端點的最短路徑,或者在某個循環未能找到符合要求的端點,則說明剩下的端點不可到達,都結束這次計算。get

  

  下面舉個例子配合圖例一步一步模擬這個過程it

    圖例約定以下:io

    

 

    初始有向圖以下:

    

 

    

  1. 把起點A加入已找到最短距離的集合當中,並更新相應距離數組和標誌位,此時找到B點距離爲2,C點距離爲6,E點距離爲10

  

 

  2. 下一步,找到距離數組的中未找到最短距離且值最小的節點。B節點距離最小,因此第二個加入到已找到最短路徑的節點是B節點,而後根據從節點B出發的全部路徑更新距離數組,從B到E距離是9,加上B本來的最短距離2,總共是11,但比距離數組中到達E點的的當前距離10更大,因此不須要更新E點的距離。但到達C點的距離是3,總共就是5,距離數組中到C點的距離爲6,咱們已經找到更短的路徑了,更新這個值。

   

  3. 接着,跟第2步一樣的步驟,找到下一個最短距離是節點C,一樣更新距離數組和標誌位,接着是節點D,而後是節點E,至此全部節點都被添加到已找到最短路徑集合當中,算法結束

  

   

 

  

整個過程如圖所示,接下來就是寫代碼咯

 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算法的理解,若有不妥之處,歡迎指出斧正。

 

 

 

尊重知識產權,轉載引用請通知做者並註明出處!

相關文章
相關標籤/搜索