1. 程式人生 > >深入理解 Dijkstra 演算法實現原理

深入理解 Dijkstra 演算法實現原理

迪傑斯特拉(Dijkstra)演算法

  • 1典型最短路徑演算法,用於計算一個節點到其他節點的最短路徑。
  • 2特點是以起始點為中心向外層層擴充套件(廣度優先搜尋思想),直到擴充套件到終點為止。

大概就是這樣一個有權圖,Dijkstra演算法可以計算任意節點其他節點的最短路徑

演算法思路

  1. 初始時,S只包含起點s;U包含除s外的其他頂點,且U中頂點的距離為”起點s到該頂點的距離”[例如,U中頂點v的距離為(s,v)的長度,然後s和v不相鄰,則v的距離為∞]。
  2. 從U中選出”距離最短的頂點k”,並將頂點k加入到S中;同時,從U中移除頂點k。
  3. 更新U中各個頂點到起點s的距離。之所以更新U中頂點的距離,是由於上一步中確定了k是求出最短路徑的頂點,從而可以利用k來更新其它頂點的距離;例如,(s,v)的距離可能大於(s,k)+(k,v)的距離。
  4. 重複步驟(2)和(3),直到遍歷完所有頂點。  

演算法圖解

1.選定A節點並初始化

2.執行上述2,3步驟,找出U集合中路徑最短的節點D 加入S集合,從U中移除節點D,更新U中各個頂點到起點A的距離,根據(AD距離 + D到B,C,E的距離< A到B,C,E 的距離)來更新U集合

3.這時候 B, C 都為3,沒關係。其實這時候他倆都是最短距離,如果從演算法邏輯來講的話,會先取到B點。 執行步驟2,3

思路就是這樣,往後就是大同小異

程式碼實現

package Test;

import java.util.ArrayList;
import java.util.List;

public class Dijkstra {
    public static final int M = 10000; // 代表正無窮
    
    public static void main(String[] args) {
        // 二維陣列每一行分別是 A、B、C、D、E 各點到其餘點的距離, 
        // A -> A 距離為0, 常量M 為正無窮
        int[][] weight1 = {{0,4,M,2,M}, //A節點到A、B、C、D、E 各點到其餘點的距離
                {4,0,4,1,M}, //B節點到A、B、C、D、E 各點到其餘點的距離
                {M,4,0,1,3}, //C節點到A、B、C、D、E 各點到其餘點的距離
                {2,1,1,0,7}, //D節點到A、B、C、D、E 各點到其餘點的距離  
                {M,M,3,7,0}  //E節點到A、B、C、D、E 各點到其餘點的距離
            };
        
        //測試A節點到其他節點的距離
        int start = 0;
        dijkstra(weight1, start);
           
    }
    /**
     * 
     * 描述:將數字轉換為對應的節點(A,B,C,D,E)
     * @param 
     * @return
     */
    public static  String getNode(int i) {
    	String node=null;
    	switch (i) {
		case 0:
			node="A";
			break;
		case 1:
			node="B";
			break;
		case 2:
			node="C";
			break;
		case 3:
			node="D";
			break;
		case 4:
			node="E";
			break;
		default:
			break;
		}
		return node;
    }
    public static void dijkstra(int[][] weight, int start) {
        // 接受一個有向圖的權重矩陣,和一個起點編號start(從0編號,頂點存在陣列中)
        // 返回一個int[] 陣列shortPath,表示從start到它的最短路徑長度
        int n = weight.length; // 頂點(0,1,2,3,4)
        //S的作用是記錄已求出最短路徑的頂點集合
        int[] s = new int[n];  
        
        //U則是記錄還未求出最短路徑的頂點集合
        List<Integer> u= new ArrayList<Integer>(); 
			for (int i=0;i<n;i++) {
				u.add(i);
		}
			
     // 儲存start到其他各點最短路徑的字串表示
        String[] path = new String[n]; 
        for (int i = 0; i < n; i++){
        	   path[i] = new String(getNode(start) + "-->" + getNode(i));//預設設定起點到各個頂點的輸出格式
        }
         
       
			
		// 初始化,第一個頂點已經求出
        s[start] = 0;
        //U中排除第一個頂點
        u.remove(Integer.valueOf(start));

        for (int count = 1; count < n; count++) { // 要加入n-1個頂點
            int k = -1; // 選出一個距離初始頂點start最近的未標記頂點
            int dmin = Integer.MAX_VALUE;
            //選擇集合中未標記過的最短路徑的節點(相當於在U中獲取最小路徑的節點)
            for (int i = 0; i < n; i++) {
                if (u.contains(i) && weight[start][i] < dmin) {
                    dmin = weight[start][i];
                    k = i;
                }
            }
            // 將選出的最短路徑節點放到集合S中,並設定該節點的路徑大小
            s[k] = dmin;
            u.remove(Integer.valueOf(k));//排除以放到S集合的節點

            // 以k為中間點,修正從start到未訪問各點的距離
            for (int i = 0; i < n; i++) {//和所有沒有標記最短路徑進行對比,更新U中的所有最短距離
                //如果 '起始點到當前點距離' + '當前點到某點距離' < '起始點到某點距離', 則更新
                if (u.contains(i)  && weight[start][k] + weight[k][i] < weight[start][i]) {
                    weight[start][i] = weight[start][k] + weight[k][i];//修正起始節點到某點的最短距離
                    path[i] = path[k] + "-->" + getNode(i);//
                }
            }
        }
        //輸出
        for (int i = 0; i < n; i++) {
            System.out.println("從" + getNode(start) + "出發到" + getNode(i) + "最短距離為:"+s[i]+",最短路徑為:" + path[i]);
        }
    }
    
}

 


結果
從A出發到A最短距離為:0,最短路徑為:A-->A
從A出發到B最短距離為:3,最短路徑為:A-->D-->B
從A出發到C最短距離為:3,最短路徑為:A-->D-->C
從A出發到D最短距離為:2,最短路徑為:A-->D
從A出發到E最短距離為:6,最短路徑為:A-->D-->C-->E