一、介紹
- 迪傑斯特拉(Dijkstra)算法是典型最短路徑算法,用於計算一個結點到其他結點的最短路徑。 它的主要特點是以起始點爲中心向外層層擴展(廣度優先搜索思想),直到擴展到終點爲止。
- 算法過程:設置出發頂點爲v,頂點集合V{v1,v2,vi…},v到V中各頂點的距離構成距離集合Dis,Dis{d1,d2,di…},Dis集合記錄着v到圖中各頂點的距離(到自身可以看作0,v到vi距離對應爲di)
- 從Dis中選擇值最小的di並移出Dis集合,同時移出V集合中對應的頂點vi,此時的v到vi即爲最短路徑
- 更新Dis集合,更新規則爲:比較v到V集合中頂點的距離值,與v通過vi到V集合中頂點的距離值,保留值較小的一個(同時也應該更新頂點的前驅節點爲vi,表明是通過vi到達的)
- 重複執行兩步驟,直到最短路徑頂點爲目標頂點即可結束
二、應用場景-最短路徑
A(2)B(3)C(9)D(10)E(4)F(6)G(0) A(7) B(12) C(0) D(17) E(8) F(13) G(9)
戰爭時期,勝利鄉有7個村莊(A, B, C, D, E, F, G) ,現在有六個郵差,從G點出發,需要分別把郵件分別送到 A, B, C , D, E, F 六個村莊
各個村莊的距離用邊線表示(權) ,比如 A – B 距離 5公里
問:如何計算出G村莊到 其它各個村莊的最短距離?
如果從其它點出發到各個點的最短距離又是多少?
三、代碼實現
import java.util.Arrays;
public class DijkstraAlgorithm {
public static void main(String[] args) {
char[] vertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
//鄰接矩陣
int[][] matrix = new int[vertex.length][vertex.length];
final short N = Short.MAX_VALUE;
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 graph = new Graph(vertex,matrix);
graph.dijkstra(2);
graph.showDijkstra();
}
}
class Graph {
//頂點數組
private char[] vertex;
//鄰接矩陣
private int[][] matrix;
//已經訪問的頂點的集合
private VisitedVertex vv;
public Graph(char[] vertex, int[][] matrix) {
this.vertex = vertex;
this.matrix = matrix;
}
/**
* 顯示結果
*/
public void showDijkstra() {
vv.showResult(vertex);
}
/**
* 顯示圖
*/
public void showGraph() {
for (int[] link : matrix) {
System.out.println(Arrays.toString(link));
}
}
/**
* 更新index下標頂點到周圍頂點的距離和周圍頂點的前驅頂點
*
* @param index 頂點下標
*/
private void update(int index) {
int len = 0;
//根據遍歷我們的鄰接矩陣的 matrix[index]行
for (int i = 0; i < matrix[index].length; i++) {
//len :出發頂點到index頂點的距離 + 從index頂點到i頂點的距離
len = vv.getDis(index) + matrix[index][i];
//如果j頂點沒有被訪問過,並且len小於出發頂點到j頂點的距離則更新
if (!vv.indexAlreadyArr(i) && len < vv.getDis(i)) {
//更新i頂點的前驅節點爲index頂點
vv.updatePre(i, index);
//更新出發頂點到i頂點的距離
vv.updateDis(i, len);
}
}
}
/**
* 迪傑斯特拉算法實現
*
* @param index 表示出發頂點對應的下標
*/
public void dijkstra(int index) {
//創建已經訪問的頂點的集合
vv = new VisitedVertex(vertex.length, index);
//更新index頂點到周圍頂點的距離和前驅頂點
update(index);
for (int i = 0; i < vertex.length; i++) {
//選擇並返回新的訪問節點
index = vv.updateArr();
//更新index頂點到周圍頂點的距離和前驅頂點
update(index);
}
}
}
/**
* 已訪問頂點集合
*/
class VisitedVertex {
/**
* 記錄各個頂點是否訪問過,未訪問:0,已訪問:1
*/
public int[] alreadyArr;
/**
* 每個下標的值爲前一個頂點的下標
*/
public int[] preVisited;
/**
* 記錄出發頂點到其他所有頂點的距離
*/
public int[] dis;
/**
* 構造器
*
* @param length 頂點的個數
* @param index 出發頂點對應的下標
*/
public VisitedVertex(int length, int index) {
//結點訪問
this.alreadyArr = new int[length];
//前驅節點
this.preVisited = new int[length];
//訪問距離
this.dis = new int[length];
//初始化 dis 數組
Arrays.fill(dis, Short.MAX_VALUE);
//設置出發頂點被訪問過
this.alreadyArr[index] = 1;
//設置出發頂點的訪問距離爲0
this.dis[index] = 0;
}
/**
* 判斷index頂點是否被訪問過
*
* @param index 頂點下標
* @return 訪問過返回true, 否則返回false
*/
public boolean indexAlreadyArr(int index) {
return alreadyArr[index] == 1;
}
/**
* 修改出發頂點到index頂點的距離
*
* @param index 頂點下標
* @param len 距離
*/
public void updateDis(int index, int len) {
dis[index] = len;
}
/**
* 修改pre這個頂點的前驅頂點爲index頂點
*
* @param pre 前驅頂點下標
* @param index 待更新頂點下標
*/
public void updatePre(int pre, int index) {
preVisited[pre] = index;
}
/**
* 獲取出發頂點到index頂點的距離
*
* @param index 頂點下標
* @return 距離
*/
public int getDis(int index) {
return dis[index];
}
/**
* 選擇並返回新的訪問頂點
*
* @return 新的訪問頂點
*/
public int updateArr() {
int min = Short.MAX_VALUE, index = 0;
//獲取最小距離的頂點
for (int i = 0; i < alreadyArr.length; i++) {
if (alreadyArr[i] == 0 && dis[i] < min) {
min = dis[i];
index = i;
}
}
//更新index頂點被訪問過
alreadyArr[index] = 1;
return index;
}
/**
* 顯示是否頂點訪問情況
*/
public void showAlreadyArr() {
System.out.println("-----------AlreadyArr-----------");
for (int i : alreadyArr) {
System.out.print(i + " ");
}
System.out.println("-----------AlreadyArr-----------");
}
/**
* 顯示結果
*
* @param vertex
*/
public void showResult(char[] vertex) {
System.out.println("-----------Result-----------");
int count = 0;
for (int i : dis) {
if (i != Short.MAX_VALUE) {
System.out.println(vertex[count] + " -> " + i);
} else {
System.out.println(" N ");
}
count++;
}
System.out.println("-----------Result-----------");
}
}