算法描述

  • 描述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[]都是最短路径长度

实现

  1. import edu.princeton.cs.algs4.IndexMinPQ;
  2. import edu.princeton.cs.algs4.Stack;
  3. public class DijkstraSP {
  4. private DirectedEdge[] edgeTo;
  5. private double[] distTo;
  6. private IndexMinPQ<Double> pq;
  7. public DijkstraSP(EdgeWeightedDigraph G, int s){
  8. edgeTo = new DirectedEdge[G.V()];
  9. distTo = new double[G.V()];
  10. pq = new IndexMinPQ<>(G.V());
  11. for (int v = 0; v < G.V(); v++)
  12. distTo[v] = Double.POSITIVE_INFINITY;
  13. distTo[s] = 0.0;
  14. pq.insert(s, 0.0);
  15. while (!pq.isEmpty()){
  16. relax(G, pq.delMin());
  17. }
  18. }
  19. private void relax(EdgeWeightedDigraph G, int v){
  20. for (DirectedEdge e: G.adj(v)){
  21. int w = e.to();
  22. if (distTo[w] > distTo[v]+e.weight()){
  23. distTo[w] = distTo[v]+e.weight();
  24. edgeTo[w] = e;
  25. if (pq.contains(w)) pq.change(w, distTo[w]);
  26. else pq.insert(w, distTo[w]);
  27. }
  28. }
  29. }
  30. public double distTo(int v){ return distTo[v];}//s->v的距离
  31. public boolean hasPathTo(int v){ return distTo[v] < Double.POSITIVE_INFINITY;}
  32. public Iterable<DirectedEdge> pathTo(int v){
  33. Stack<DirectedEdge> stack = new Stack<>();
  34. for(DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()])
  35. stack.push(e);
  36. return stack;//后根入栈
  37. }
  38. }

示例

Dijkstra.jpg

算法分析

  • G(V,E)中使用Dijkstra算法构建SPT所需空间和V成正比,时间和ElogE成正比(最坏)

证明:与使用索引优先队列的Prim一致


任意两顶点间最短路径

  • 与算法4.6中建立传递闭包获得顶点对可达性类似,通过建立DijkstraSP[]判读顶点s,t直接知否有最短路径,已经最短路径大小
  1. public class DijkstraAllPairsSP {
  2. private DijkstraSP[] all;
  3. public DijkstraAllPairsSP(EdgeWeightedDigraph G){
  4. all = new DijkstraSP[G.V()];
  5. for (int v = 0; v < G.V(); v++){
  6. all[v] = new DijkstraSP(G, v);
  7. }
  8. }
  9. //返回s->t最短路径
  10. Iterable<DirectedEdge> path(int s, int t){ return all[s].pathTo(t);}
  11. //返回s->t最短路径大小
  12. double dist(int s, int t){ return all[s].distTo(t); }
  13. }