1. 程式人生 > 實用技巧 >迪杰特斯拉演算法

迪杰特斯拉演算法

迪傑斯特拉演算法的基本思路

  1. 找出最便宜的頂點,即距離出發頂點距離最近的頂點,作為新的出發頂點
  2. 更新該頂點的鄰居的開銷
  3. 重複(1)(2)步驟,直到對每個頂點都做了此操作
  4. 計算最終路徑

如何對下面的圖使用迪傑斯特拉演算法:
每條邊上標識的數字都表示,兩個頂點之間的距離。

演算法詳解

假設要求從 A 到 D 的最短路徑,步驟如下:

準備工作:

          在計算前,需要做一些準備工作,列出每個節點的開銷:
          在執行迪傑斯特拉演算法的過程中,將不斷的更新這個表。為了計算最終路徑,還需要在表中新增表示父頂點(前驅頂點)的列

第一步:找出最便宜的頂點,作為新的出發頂點(紅色標識表示該頂點已經被訪問過,選擇新的出發頂點是不再考慮該頂點)
站在出發頂點A,與A直連的頂點有:

  • C(A到C的距離為7)
  • B(A到B的距離為5)
  • G(A到G的距離為2)

顯然,A到G的距離最近,則G為最便宜的頂點。
此時,由於你還不知道從A出發到其它非直連的頂點的距離是多少,因此,我們都將他們暫時設為無窮大

第二步:更新頂點G的鄰居的開銷

計算從出發頂點A出發,經過G頂點,到G的各個鄰居(與G直連的頂點)的開銷

G頂點的鄰居有:

  • 頂點A(A被訪問過)
  • 頂點B(G到B的距離為3)
  • 頂點E(G到E的距離為4)
  • 頂點F(G到F的距離為6)

再執行
第一步:
下一個最便宜的頂點是B

第二步:更新頂點B的鄰居的開銷
B頂點的鄰居有:
- 頂點A (A被訪問過)
- 頂點G(G被訪問過)
- 頂點D (B到D的距離為9)

再執行
第一步:
下一個最便宜的頂點是E

第二步:更新頂點E的鄰居的開銷
E頂點的鄰居有:
- 頂點C(E到C的距離為8)
- 頂點G(G被訪問過)
- 頂點F (E到F的距離為5)

再執行
第一步:
下一個最便宜的頂點是C

第二步:更新頂點C的鄰居的開銷
C頂點的鄰居有:
- 頂點A(A被訪問過)
- 頂點E(G被訪問過)

再執行
第一步:
下一個最便宜的頂點是F

第二步:更新頂點F的鄰居的開銷
F頂點的鄰居有:
- 頂點E(E被訪問過)
- 頂點G(G被訪問過)
- 頂點D(F到D的距離為4)

再執行
第一步:
下一個最便宜的頂點是D

第二步:更新頂點D的鄰居的開銷
D頂點的鄰居有:
- 頂點B(B被訪問過)
- 頂點F(F被訪問過)

計算最終路徑(求從 A 到 D 的最短路徑):

頂點D的前驅頂點為F
頂點F的前驅頂點為G
頂點G的前驅頂點為A(A為出發頂點,至此得出最短路徑)

所以:
A到D的最短路徑長度為12;
最短路徑為:A——>G——>F——>D

程式碼

`package com.dyt.algorithmdemo.dijkstra;

import java.util.*;

/**

  • 迪杰特斯拉演算法
    */
    public class DijkstraAlgorithm {

    public static void main(String[] args) {
    //頂點陣列
    String[] vertex = {"A", "B", "C", "D", "E", "F", "G"};
    //鄰接矩陣
    int[][] matrix = new int[vertex.length][vertex.length];

     final int N = 65535;   //表示不可連線,沒有路
    
     //初始化鄰接矩陣
     matrix[0] = new int[]{N, 5, 7, N, N, N, 2};
     matrix[1] = new int[]{5, N, N, 9, N, N, 3};
     matrix[2] = new int[]{7, N, N, N, 8, N, N};
     matrix[3] = new int[]{N, 9, N, N, N, 4, N};
     matrix[4] = new int[]{N, N, 8, N, N, 5, 4};
     matrix[5] = new int[]{N, N, N, 4, 5, N, 6};
     matrix[6] = new int[]{2, 3, N, N, 4, 6, N};
    
     //建立圖物件
     Graph graph = new Graph(vertex, matrix);
     graph.showGraph();
    
     graph.djs(0);
     graph.showDijkstra();
     //輸出路徑
     graph.printPath(0, 3);
    

    }

}

/**


  • */
    class Graph {

    //頂點陣列
    private String[] vertex;
    //鄰接矩陣
    private int[][] matrix;
    //訪問過的頂點
    private VisitedVertex visitedVertex;

    public Graph(String[] vertex, int[][] matrix) {
    this.vertex = vertex;
    this.matrix = matrix;
    }

    //顯示圖
    public void showGraph() {
    for (int[] link : matrix) {
    System.out.println(Arrays.toString(link));
    }
    }

    /**

    • 迪杰特斯拉演算法核心程式碼
    • @param index
      */
      public void djs(int index) {
      visitedVertex = new VisitedVertex(vertex.length, index);
      update(index); //更新index頂點到周圍頂點的距離和前驅頂點
      for (int j = 1; j < vertex.length; j++) {
      index = visitedVertex.updateArr();//選擇並返回新的訪問頂點
      update(index);// 更新index頂點到周圍頂點的距離和前驅頂點
      }
      }

    //更新index下標頂點到周圍頂點的距離和周圍頂點的前驅頂點
    public void update(int index) {
    int len = 0;
    //遍歷我們的鄰接矩陣matrix[index]行
    for (int j = 0; j < matrix[index].length; j++) {
    //len : 出發頂點到index頂點的距離 + 從index頂點到j頂點距離的和
    len = visitedVertex.getDis(index) + matrix[index][j];
    //如果j頂點沒有被訪問過 && 距離小於從出發頂點到j點的距離
    if (!visitedVertex.in(j) && len < visitedVertex.getDis(j)) {
    visitedVertex.updatePre(j, index);//更新j頂點的前驅為index頂點
    visitedVertex.updateDis(j, len); //更新出發點到頂點j的距離
    }
    }
    }

    /**

    • 輸出路徑

    • @param startIndex 起點索引

    • @param endIndex 終點索引
      */
      public void printPath(int startIndex, int endIndex) {
      int[] dis = visitedVertex.getDis();
      int[] pre_visited = visitedVertex.getPre_visited();

      String startVertex = this.vertex[startIndex];
      String endVertex = this.vertex[endIndex];

      //距離
      System.out.println(startVertex + "到" + endVertex + "的最短距離為:" + dis[endIndex]);

      //路徑
      List path = new ArrayList<>();
      path.add(vertex[endIndex]);
      while (true) {
      endIndex = pre_visited[endIndex];
      path.add(vertex[endIndex]);
      if (endIndex == startIndex) {
      break;
      }
      }
      Collections.reverse(path);
      String pathInfo = "";
      for (int i = 0; i < path.size(); i++) {
      pathInfo = path.get(i);
      if (i != path.size() - 1) {
      pathInfo = pathInfo + "——>";
      }
      System.out.print(pathInfo);
      }
      System.out.println();
      }

    public void showDijkstra() {
    visitedVertex.show();
    }

}

/**

  • 已訪問過的頂點
    */
    class VisitedVertex {

    //記錄各個頂點是否訪問過。1,表示訪問過;0,表示未訪問過
    public int[] already_arr;
    //每個下標對應的值為前一個頂點的下標
    public int[] pre_visited;
    //記錄出發點到其他所有頂點的距離,如G點為出發點,就會記錄G到其他頂點的距離
    public int[] dis;

    public int[] getPre_visited() {
    return pre_visited;
    }

    public int[] getDis() {
    return dis;
    }

    /**

    • @param length 頂點個數
    • @param index 出發節點索引
      */
      public VisitedVertex(int length, int index) {
      this.already_arr = new int[length];
      this.pre_visited = new int[length];
      this.dis = new int[length];
      Arrays.fill(dis, 65535);
      //設定出發節點訪問過
      this.already_arr[index] = 1;
      //設定出發頂點的前驅頂點為自己
      this.pre_visited[index] = index;
      //設定出發頂點的訪問距離為0
      this.dis[index] = 0;
      }

    /**

    • 判斷index頂點是否被訪問過
    • @param index
    • @return 訪問過返回true,否則返回false
      */
      public boolean in(int index) {
      return already_arr[index] == 1;
      }

    /**

    • 更新出發頂點到index頂點的距離
    • @param index
    • @param distance
      */
      public void updateDis(int index, int distance) {
      dis[index] = distance;
      }

    /**

    • 更新index這個頂點的前驅頂點為pre
    • @param index
    • @param pre
      */
      public void updatePre(int index, int pre) {
      pre_visited[index] = pre;
      }

    /**

    • 返回出發頂點到index頂點的距離
    • @param index
    • @return
      */
      public int getDis(int index) {
      return dis[index];
      }

    /**

    • 繼續選擇並返回新的訪問頂點, 比如這裡的 G 完後,就是 A 點作為新的訪問頂點(注意不是出發頂點)
    • @return
      */
      public int updateArr() {
      int min = 65535, index = 0;
      for (int i = 0; i < already_arr.length; i++) {
      //如果頂點i沒有被訪問過 &&
      if (already_arr[i] == 0 && dis[i] < min) {
      min = dis[i];
      index = i;
      }
      }
      //更新index頂點被訪問過
      already_arr[index] = 1;
      return index;
      }

    public void show() {
    System.out.println("");
    //輸出已訪問頂點already_arr
    System.out.println("
    已訪問頂點already_arr
    ");
    for (int i : already_arr) {
    System.out.print(i + " ");
    }
    System.out.println();
    System.out.println("
    前驅頂點陣列pre_visited");
    //輸出前驅頂點陣列pre_visited
    for (int i : pre_visited) {
    System.out.print(i + " ");
    }
    System.out.println();
    System.out.println("
    輸出距離陣列========");
    //輸出距離陣列
    for (int i : dis) {
    System.out.print(i + " ");
    }
    System.out.println();
    }

}
`