算法描述
- 描述1:初始化distTo[s]为0,其余distTo[]为∞,之后逐个将distTo[]中最小非树结点放松,直到所有结点都在树中或所有非树结点distTo[]为∞(s不可达)
- 描述2:初始化后从s开始放松:将与s间隔一条边的顶点加入队列中,按权值小到大对所有间隔一条边结点都进行放松,然后是间隔两条边…结束条件同上
数据结构
- distTo[]:标记s->w的最短距离
- edgeTo[]:标记s->w最短路径的最后一条边
- IndexMinPQ:以”w”为索引,distTo[w]为键的索引优先队列,delMin()保证可以删除并返回路径最短的顶点
算法证明
证明:若v是起点可达的,v被放松时一定满足distTo[w]<=distTo[v]+e.weight(),放松v前的结点时,delMin()保证了distTo[v]一定是最小的,不会再改变,则distTo[w]只会变小,如此对每个顶点distTo[]都是最小的,且满足distTo[w]<=distTo[v]+e.weight(),根据最短路径最优性条件(书P420),则得到的distTo[]都是最短路径长度
实现
import edu.princeton.cs.algs4.IndexMinPQ;
import edu.princeton.cs.algs4.Stack;
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<>(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){ return distTo[v];}//s->v的距离
public boolean hasPathTo(int v){ return distTo[v] < Double.POSITIVE_INFINITY;}
public Iterable<DirectedEdge> pathTo(int v){
Stack<DirectedEdge> stack = new Stack<>();
for(DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()])
stack.push(e);
return stack;//后根入栈
}
}
示例
算法分析
- G(V,E)中使用Dijkstra算法构建SPT所需空间和V成正比,时间和ElogE成正比(最坏)
证明:与使用索引优先队列的Prim一致
任意两顶点间最短路径
- 与算法4.6中建立传递闭包获得顶点对可达性类似,通过建立DijkstraSP[]判读顶点s,t直接知否有最短路径,已经最短路径大小
public class DijkstraAllPairsSP {
private DijkstraSP[] all;
public DijkstraAllPairsSP(EdgeWeightedDigraph G){
all = new DijkstraSP[G.V()];
for (int v = 0; v < G.V(); v++){
all[v] = new DijkstraSP(G, v);
}
}
//返回s->t最短路径
Iterable<DirectedEdge> path(int s, int t){ return all[s].pathTo(t);}
//返回s->t最短路径大小
double dist(int s, int t){ return all[s].distTo(t); }
}