Dijkstra算法求解最短路徑

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) ;
	}
	
}
程序結果如下





發佈了48 篇原創文章 · 獲贊 98 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章