Dijkstra算法是由荷蘭計算機科學家狄克斯特拉於1959
年提出的,因此又叫狄克斯特拉算法。是從一個頂點到其餘各頂點的最短路徑算法,解決的是有向圖中最短路徑問題。迪傑斯特拉算法主要特點是以起始點爲中心向外層層擴展,直到擴展到終點爲止。
假設存在無向網(如下所示),希望計算從點A到其他各個頂點的最短距離
令R = {}表示已計算最短路徑的點的集合,S ={A,B,C,D,E,F}表示還未計算的點的集合。數組closeDis表示起點到其他任何頂點的最短距離;數組prePoint表示該點的最短路徑所經過的上一個點。取A作爲起始結點,然後尋找離A最近的頂點C作爲新的拓展結點並加入到已求解集合,從A->C的路徑開始尋找,若到其他頂點的距離小於舊記錄,則更新並保存最近的距離,接着尋找離C最近的頂點B作爲新的拓展結點,不斷循環,直到所有的頂點都加入到已求解集合裏,程序結束。
下邊通過手工尋找最短路徑來理解算法的運行過程
有了prePoint數組,就可以通過迭代不斷尋找上一個結節,直到找到起點位置,從而可得起點到任何一個頂點的完整路徑。以尋找點F的完整路徑爲例,由 prePoint={0,2, 0, 2,2, 3}可知,F的上一個結點爲D,而D的上一個結點爲C,C的上一個結點爲A,故F的完整路徑爲A->C->D->F。
完整的源代碼如下
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
public class DijkstraShortestPath {
private static final int MIN_DIS = 0;
private static final int MAX_DIS = 99;
// 圖的鄰接矩陣
int[][] matrix;
// 起始點
int startIndex;
// 保存起始點到其它點的最短距離
int[] closeDis;
//保存最優路徑所經過的上一個點
int[] prePoint;
// 用來存放已經找到最短路徑的點的集合
Set<Integer> foundSet = new HashSet<Integer>();
public DijkstraShortestPath(int[][] matrix, int start) {
this.matrix = matrix;
this.startIndex = start;
this.closeDis = new int[matrix.length];
this.prePoint = new int[matrix.length];
}
/**
* 算法的核心代碼,尋找最短路徑
*/
public void findPath() {
// 把鄰接矩陣第startIndex行的數據作爲初始值
for (int i = 0; i < matrix.length; i++) {
closeDis[i] = matrix[startIndex][i];
}
//從起點開始找,將起點作爲當前拓展結點
int expandNode = startIndex;
foundSet.add(startIndex);
while (foundSet.size() != matrix.length) {
int nearestNode = findNearNode(expandNode);//尋找離拓展結點最近的結點
//遍歷各個頂點,如果發現距起點更小的距離,則更新並保存
for (int i = 0; i < matrix.length; i++) {
if (!foundSet.contains(i) && matrix[nearestNode][i] != MAX_DIS
&& matrix[nearestNode][i] + closeDis[nearestNode] < closeDis[i] ){
closeDis[i] = matrix[nearestNode][i] + closeDis[nearestNode];
prePoint[i] = nearestNode;
}
}
// 放入foundSet
foundSet.add(nearestNode);
expandNode = nearestNode;
}
}
/**
* 打印從起始點到所有點的最短距離
*/
public void printDistance(){
System.err.println("使用Dijkstra尋找最短路徑");
for(int i=0;i<closeDis.length;i++){
System.err.println("從"+renameNode(startIndex)+"到"+renameNode(i)+",總長度爲"+closeDis[i]+",路徑爲"+printPath(i));
}
}
/**
* 返回當前最小距離的點(必須不包含在findedSet中)
*/
private int findNearNode(int nodeIndex) {
int min = Integer.MAX_VALUE;
int minIndex = -1;
int[] subMatrix = matrix[nodeIndex];
for(int i=0;i<subMatrix.length;i++){
if(!foundSet.contains(i) && subMatrix[i] < min){
min = subMatrix[i];
minIndex = i;
}
}
return minIndex;
}
public static void main(String[] args) {
int[][] weightMatrix = new int[][] { { MIN_DIS,7,3, MAX_DIS,MAX_DIS,MAX_DIS}, { 7,MIN_DIS,2,4,MAX_DIS,MAX_DIS},
{3,2,MIN_DIS,3,4,MAX_DIS}, { MAX_DIS,4,3,MIN_DIS,2,3},
{MAX_DIS,MAX_DIS,4,2,MIN_DIS,4 }, { MAX_DIS,MAX_DIS,MAX_DIS,2,4,MIN_DIS}};
System.out.println("圖的鄰接矩陣爲");
printAdjacentMatrix(weightMatrix);
DijkstraShortestPath path = new DijkstraShortestPath(weightMatrix, 0);
path.findPath();
path.printDistance();
}
/**
* 打印鄰接矩陣
*/
private static void printAdjacentMatrix(int[][] inputMatrix){
final int nodeCount = inputMatrix.length;
for(int i=0;i<nodeCount;i++){
for(int j=0;j<nodeCount;j++){
System.out.print(inputMatrix[i][j]+" "+"\t\t");
}
System.out.println();
}
}
/**
* 打印從起點到終點的完整路徑
*/
private String printPath(int nodeIndex){
int firstIndex = nodeIndex;
LinkedList<String> paths = new LinkedList<String>();
while(firstIndex > 0){//不斷尋找上一個結點,直到起點
paths.offerFirst(renameNode(firstIndex));//從隊首加入元素
firstIndex = prePoint[firstIndex];
}
paths.offerFirst(renameNode(startIndex));//加上起點位置
return paths.toString();
}
private String renameNode(int index){
char c =(char) (97+index); //97爲字符'a'的ascii值
return String.valueOf(c) ;
}
}
程序結果如下