1. 程式人生 > >最短路徑之Dijkstra演算法的概念與實現

最短路徑之Dijkstra演算法的概念與實現

基本概念

要找出最短路徑,其實就是從起點遍歷所有能到達的頂點,然後計算他們的權重。Dijkstra演算法核心在於邊的鬆弛(relax),可以想象成一根繃緊的橡皮筋,讓它放鬆下來。即是計算源點(s)經過當前點(v)到目標點(w)的權重,如果比目標點(w)之前的權重要小,就替換掉。最終的結果就是生成一顆最小路徑樹。這個演算法和prim演算法非常相似,甚至就是prim即時演算法的變種。如果加權無向圖和加權有向圖的邊和權重對應,最短路徑樹和最小生成樹其實是等價的。
Dijkstra演算法並不能處理權重為負數的邊。

實現

distTo初始為無窮大意味著還沒有訪問過。如果頂點訪問完後也是無窮大,就意味著不能到達。

/**
 * 迪傑斯塔拉演算法
 * @author yuli
 *
 */
public class Dijkstra implements Paths{
    private Edge[] edgeTo;//記錄邊的路徑,從起始點到某個點,
    private double[] distTo;//到這個頂點的權重
    private int s;//開始的頂點
    private IndexMinPQ<Double> pq;//用來儲存到最短路徑樹的最小邊,也就是離最短路徑樹最近的邊
    public Dijkstra(EdgeWeightedDiGraph graph,int
s) { edgeTo = new Edge[graph.getV()];//初始化頂點路徑 distTo = new double[graph.getV()];//初始化到這個頂點的距離 pq= new IndexMinPQ<>(graph.getV()); this.s = s; //將起點到這個頂點的距離初始化為無窮大。 for(int i = 0;i<graph.getV();i++){ distTo[i] = Double.POSITIVE_INFINITY; } distTo[0
]= 0.0d; pq.insert(s, 0.0); while(!pq.isEmpty()){ relax(graph,pq.delMin()); } } /** * 鬆弛邊,鬆弛就是讓權重變小 */ private void relax(EdgeWeightedDiGraph graph,int v){ Iterable<Edge> adj = graph.adj(v); for (Edge edge : adj) { int w = edge.to();//要鬆弛的目標頂點 //如果目標頂點的權重大於當前頂點的權重+當前邊的權重,就替換 if(distTo[w] > distTo[v] + edge.getWeight()){ distTo[w] = distTo[v] + edge.getWeight(); edgeTo[w] = edge;//到這條邊的最小權重 //通過索引優先佇列來減少比較次數 if(pq.contains(w)){ pq.changeKey(w, distTo[w]);; }else{ pq.insert(w, distTo[w]); } } } } /** * 取得最短路徑 * @param v * @return */ public Iterable<Integer> pathTo(int v){ if(hasPathTo(v)){ throw new RuntimeException("沒有此路徑"); } Stack<Integer> stack = new Stack<>(); while(v != s){ Edge e = edgeTo[v]; stack.push(v); v = e.form(); } stack.push(s); return stack; } /** * 獲取最短路徑的權重權重 * @return */ public double weight(int v){ return distTo[v]; } public boolean hasPathTo(int v) { return distTo[v] == Double.POSITIVE_INFINITY; } }