Dijkstra算法
Dijkstra算法能夠解決邊權重非負的加權有向圖的單起點最短路徑問題。
在之前,我們討論過尋找加權無向圖中的最小生成樹的Prim算法:構造最小生成樹的每一步都向這棵樹中添加一條新的邊。
Dijkstra算法採用了類似的方法來計算最短路徑樹。首先將 distTo[] 最小的非樹頂點放鬆並加入樹中,如此直到所有的頂點都在樹中或者所有非樹頂點的 distTo[] 值均爲無窮大。
數據結構
要實現 Dijkstra算法,除了 distTo[] 和 edgeTo[] 數組之外還需要一條 索引優先隊列 pq,以保存需要被放鬆的頂點並確認下一個被放鬆的頂點。
IndexMinPQ 可以將索引和鍵(優先級)關聯起來,並且可以刪除並返回優先級最低的索引。在這裏,只要將頂點v 和 distTo[v] 關聯起來就可以得到 Dijkstra 算法的實現。
算法步驟:
1)distTo[s] = 0, distTo[v] = INFINITY (v≠s)
2)將 distTo[] 中離頂點 s 最近的非樹頂點放鬆, 並加入到樹中
3)重複2,直到所有頂點都在樹中或者所有的非樹頂點的 distTo[] 值均爲無窮大
算法實現
在 relax() 方法中添加了一行語句來處理以下兩種情況:
1、邊的 to() 得到的頂點不在優先隊列中,此時需要使用 insert() 方法將它加入到優先隊列中
2、它已經在優先隊列中,且優先級需要被降低,此時需要使用 change() 方法實現
思考 Dijkstra算法的另一種方式就是將它和 Prim算法相比較。兩種算法都會用添加邊的方式構造一棵樹:Prim算法每次添加的都是 離樹最近 的非樹頂點;Dijkstra算法每次添加的都是 離起點最近 的非樹頂點。
/*
* 單起點最短路徑的 Dijkstra算法
*/
public class DijkstraSP
{
private DirectedEdge[] edgeTo;
private double[] distTo;
private IndexMinPQ<Double> pq; //優先隊列
public DijkstraSP(EdgeWeightedDigraph G, int s)
{
edgeTo = new DirectedEdge[G.V()];
distTo = new double[G.V()];
pq = new IndexMinPQ<Double>(G.V());
for (int v = 0; v < G.V(); v++)
distTo[v] = Double.POSITIVE_INFINITY; //初始化距離爲正無窮
distTo[s] = 0.0;
pq.insert(s, 0.0);
while (!pq.isEmpty()) //直到所有頂點都已加入到最短路徑中
relax(G, pq.delMin()) //鬆弛,每次鬆弛都從隊列中刪除一個點,並將邊加入到最短路徑中
}
private void relax(EdgeWeightedDigraph G, int v)
{
for (DirectedEdge e : G.adj(v))
{
int w = e.to();
if (distTo[w] > distTo[v] + e.weight())
{
distTo[w] = distTo[v] + e.weight();
edgeTo[w] = e;
if (pq.contains(w)) pq.change(w, distTo[w]);
else pq.insert(w, distTo[w]);
}
}
}
public double distTo(int v) // standard client query methods
public boolean hasPathTo(int v) // for SPT implementatations
public Iterable<Edge> pathTo(int v) // (See page 649.)
}
任意頂點對的最短路徑
給定兩點的最短路徑:給定一幅加權有向圖和一個起點 s 、一個終點 t,是否存在一條從 s 到 t 的路徑?如果有,找出最短的那條路徑。
算法構造了 DijkstraSP對象 的數組,每個元素都將相應的頂點作爲起點。在用例進行查詢時,代碼會訪問起點所對應的單點最短路徑對象,並將目的頂點作爲參數進行查詢。
/*
* 任意頂點對之間的最短路徑
*/
public class DijkstraAllPairsSP {
private DijkstraSP[] all;
DijkstraAllPairsSP(EdgeWeightedDigraph G)
{
all = new DijkstraSP[G.V()];
for (int v = 0; v < G.V(); v++)
all[v] = new DijkstraSP(G, v);
}
Iterable<Edge> path(int s, int t) {
return all[s].pathTo(t);
}
double dist(int s, int t) {
return all[s].distTo(t);
}
}