一. 迪傑特斯拉算法
迪傑斯特拉算法的基本思路
迪傑斯特拉算法的基本思路:
- 找出最便宜的頂點,即距離出發頂點距離最近的頂點,作為新的出發頂點
- 更新該頂點的鄰居的開銷
- 重復(1)(2)步驟,直到對每個頂點都做了此操作
- 計算最終路徑
如何對下面的圖使用迪傑斯特拉算法
每條邊上標識的數字都表示,兩個頂點之間的距離。
二. 算法詳解
假設要求從 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]);//路徑
Listpath = 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();
}
}
`