1. 程式人生 > 其它 >最短路徑Dijkstra演算法

最短路徑Dijkstra演算法

最短路徑

最短路徑的性質:

  • 路徑是有向的
  • 權重不一定等價於距離,權重也可以指時間,花費或者其他
  • 並不是所有頂點都是可達的
  • 負權重會使得問題更復雜(Dijkstra演算法不適用於這種情況)
  • 最短路徑一般都是簡單的,這裡演算法會忽略構成環的零權重邊,找到的最短路徑都不會有環
  • 最短路徑不一定是唯一的
  • 可能存在平行邊和自環

最短路徑樹:給出起點s,計算結果是一顆最短路徑樹(Shortest Path Tree SPT),它包含了頂點s到所有可達頂點的最短路徑

資料結構:

  • 最短路徑樹中的邊edgeTo[],edgeTo[v]表示s到v的最短路徑中的最後一條邊
  • 到達起點的距離distTo[],distTo[v]表示s到v的已知最短路徑長度

Dijkstra 演算法

取自<演算法>第四版-演算法4.9

該演算法類似Prim及時演算法


    private double[] distTo;          // distTo[v] = distance  of shortest s->v path
    private DirectedEdge[] edgeTo;    // edgeTo[v] = last edge on shortest s->v path
    private IndexMinPQ<Double> pq;	  // priority queue of vertices

	public DijkstraSP(EdgeWeightedDigraph G, int s) { 
		// 起點到各個頂點v的距離
        distTo = new double[G.V()];
        // 起點到各個頂點v最短路徑中的最後一條邊
        edgeTo = new DirectedEdge[G.V()];

        // 初始化起點s到各個頂點的距離為無窮最大值
        for (int v = 0; v < G.V(); v++) {
            distTo[v] = Double.POSITIVE_INFINITY;
        }
        distTo[s] = 0.0;

        // 從s到頂點v的權重為優先順序
        pq = new IndexMinPQ<Double>(G.V());
        // s為起點
        pq.insert(s, distTo[s]);
        while (!pq.isEmpty()) {
            // 取出頂點v
            int v = pq.delMin();
            // 遍歷頂點v的各個邊
            for (DirectedEdge e : G.adj(v)) {
                relax(e);
            }
        }
	}

    // 鬆弛一個頂點,判斷起點s到頂點w的最短路徑是否是s -> v -> w
    private void relax(DirectedEdge e) {
        // 邊e為從頂點v到頂點w的有向邊
        int v = e.from(), w = e.to();
        // 如果s到w的距離大於s到v + v到w的距離,則更新該距離,並將頂點w存入優先順序佇列
        if (distTo[w] > distTo[v] + e.weight()) {
            distTo[w] = distTo[v] + e.weight();
            edgeTo[w] = e;
            // 如果佇列中已存在頂點w,則需要更新s到頂點w的距離,也即是更新權重
            if (pq.contains(w)) {
                pq.decreaseKey(w, distTo[w]);
            } else {
                pq.insert(w, distTo[w]);
            }               
        }
    }

    // 判斷起點s到頂點v是否可達
    public boolean hasPathTo(int v) {
        return distTo[v] < Double.POSITIVE_INFINITY;
    }

	// 獲取起點s到頂點v的最短路徑
    public Iterable<DirectedEdge> pathTo(int v) {
        if (!hasPathTo(v)) {
            return null;
        }
        Stack<DirectedEdge> path = new Stack<DirectedEdge>();
        for (DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()]) {
            path.push(e);
        }
        return path;
    }

空間複雜度:V

時間複雜度:ElogV

給定兩點s->t的最短路徑:可使用Dijkstra演算法並在優先佇列中取到t之後終止搜尋

任意頂點之間的最短路徑:遍歷各個頂點構造最短路徑樹,時間及空間複雜度均為EVlogV

加權無環圖

加權無環圖:加權有向圖中不含有有向環的圖

可線上性時間內解決加權無環圖中的最短路徑問題,比Dijkstra演算法更快

  • 可線上性時間內解決單點最短路徑問題
  • 能夠處理負權重的邊

待補充...

負權重的加權有向圖

負權重的加權有向圖:可能含有環也可能含有負權重的邊的加權有向圖

Bellman-Ford演算法:時間複雜度EV,空間複雜度V

大佬自行研究去吧,我該curd去了