1. 應(yīng)用場景:
假如有七個村莊(ABCDEFG),有個人從G點出發(fā),到其他六個村莊的最短路徑分別是多少?到A、B、F、E只有一條路,沒得選,但是到C有兩條路,可以是2 + 7
,也可以是8 + 4
,到D點可以是3 + 9
,也可以是6 + 4
。圖上標明了距離我們當(dāng)然一看就知道怎么選,那么如何能讓程序選擇最短的路徑呢?
迪杰斯特拉算法就是求最短路徑的經(jīng)典算法。它的主要思想就是以起始點向外層層擴展,用廣度優(yōu)先的思想,直到擴展到終點為止。
2. 算法步驟:
以上面的例子,從G開始處理,來講解這個過程:
String[] vertexs = {"A", "B", "C", "D", "E", "F", "G"};
A B C D E F G
A{N, 5, 7 ,N, N, N, 2}
B{5, N, N ,9, N, N, 3}
C{7, N, N ,N, 8, N, N}
D{N, 9, N ,N, N, 4, N}
E{N, N, 8 ,N, N, 5, 4}
F{N, N, N ,4, 5, N, 6}
G{2, 3, N ,N, 4, 6, N}
already_arr
,長度為頂點的個數(shù)。數(shù)組里面,0表示未訪問過該頂點,1表示訪問過該頂點。初始的時候其他都是0,G頂點的是1。int[] already_arr = {0, 0, 0, 0, 0, 0, 1};
dis
,長度也是頂點的個數(shù)。該數(shù)組記錄的是當(dāng)前頂點到其他頂點的距離,初始的時候是一個較大的數(shù),到自己的距離就是0。int[] dis = {N, N, N, N, N, N, 0};
pre_visited
,長度也是頂點的個數(shù)。數(shù)組表示頂點的到前驅(qū)頂點的距離,初始時候都是0。int[] pre_visited = {0, 0, 0, 0, 0, 0, 0};
dis
數(shù)組,如下:int[] dis = {2, 3, N, N, 4, 6, 0};
pre_visited
數(shù)組變成了:int[] pre_visited = {6, 6, 0, 0, 6, 6, 0};
dis
數(shù)組中。3. 代碼實現(xiàn):
class Graph{
String[] vertexs; // 存放頂點
int[][] edges; // 鄰接矩陣,存放邊
public Graph(String[] vertexs, int[][] edges) {
this.vertexs = vertexs;
this.edges = edges;
}
}
class VisitedVertex{
private static final int N = 999;
public int[] already_arr;
public int[] pre_visited;
public int[] dis;
/**
* 構(gòu)造器
* @param length 頂點的個數(shù)
* @param index 出發(fā)點的索引
*/
public VisitedVertex(int length, int index) {
this.already_arr = new int[length];
this.pre_visited = new int[length];
this.dis = new int[length];
// 初始化dis數(shù)組
Arrays.fill(dis, N);
dis[index] = 0;
}
/**
* 判斷index對應(yīng)的頂點是否被訪問過
* @param index
* @return
*/
public boolean isVisited(int index) {
return already_arr[index] == 1;
}
/**
* 更新dis數(shù)組,索引為index的值更新為len
* @param index
* @param len
*/
public void updateDis(int index, int len) {
dis[index] = len;
}
/**
* 更新前驅(qū)頂點
* @param index
* @param val
*/
public void updatePre(int index, int val) {
pre_visited[index] = val;
}
/**
* 將index設(shè)置為已訪問
* @param index
*/
public void updateAlreadyArr(int index) {
already_arr[index] = 1;
}
/**
* 返回出發(fā)頂點到index這個頂點的距離
* @param index
* @return
*/
public int getDis(int index) {
return dis[index];
}
/**
* 返回要處理的下一個頂點
* @return
*/
public int nextVertexs() {
int index = 0;
int min = N;
for (int i=0; i< already_arr.length; i++) {
if (!isVisited(i) && dis[i] < min) {
min = dis[i];
index = i;
}
}
updateAlreadyArr(index);
return index;
}
public void printArr() {
System.out.println(Arrays.toString(this.dis) + " dis");
System.out.println(Arrays.toString(this.pre_visited) + " pre_visited");
System.out.println(Arrays.toString(this.already_arr) + " already_arr");
}
}
/**
* 迪杰斯特拉算法實現(xiàn)
* @param index 出發(fā)頂點的下標
*/
public void dsj(int index) {
VisitedVertex visitedVertex = new VisitedVertex(this.vertexs.length, index);
updateArrs(index, visitedVertex);
// 下一個要處理的頂點
for (int i=1; i<vertexs.length; i++) {
index = visitedVertex.nextVertexs();
updateArrs(index, visitedVertex);
}
visitedVertex.printArr();
}
/**
* 拿到鄰接矩陣的第index行,根據(jù)這一行對三個數(shù)組進行更新
* @param index
* @param visitedVertex
*/
private void updateArrs(int index, VisitedVertex visitedVertex) {
int[] tempArr = this.edges[index];
// distance表示出發(fā)頂點到索引為index的這個頂點的距離
int distance = 0;
for (int i=0; i< tempArr.length; i++) {
// 先前的距離 + 本次遍歷得到的距離
distance = visitedVertex.getDis(index) + tempArr[i];
if (distance < visitedVertex.getDis(i) && !visitedVertex.isVisited(i)) {
visitedVertex.updateDis(i, distance);
visitedVertex.updatePre(i, index);
visitedVertex.updateAlreadyArr(index);
}
}
}
測試方法:
public class DijkstraDemo {
public static final int N = 999;
public static void main(String[] args) {
String[] vertexs = {"A", "B", "C", "D", "E", "F", "G"};
int[][] edges = {
{N, 5, 7 ,N, N, N, 2},
{5, N, N ,9, N, N, 3},
{7, N, N ,N, 8, N, N},
{N, 9, N ,N, N, 4, N},
{N, N, 8 ,N, N, 5, 4},
{N, N, N ,4, 5, N, 6},
{2, 3, N ,N, 4, 6, N}
};
Graph graph = new Graph(vertexs, edges);
graph.dsj(6);
}
}
運行后得到結(jié)果如下:
dis
數(shù)組就是我們想要的結(jié)果,即對應(yīng)的頂點G到各個點的最短距離。
掃描二維碼