4.4 最短路径


模型:加权有向图

问题:单点最短路径:给定加权有向图和s,求”s到给定v是否存在有向路径,若有,找出权重最小的”

4.4.1最短路径性质

  • 并非所有顶点都可达
  • 最短路径不一定唯一
  • 最短路径树:SPT,包含s到所以可达顶点的最短路径

4.4.2 加权有向图

加权有向边API:public class DirectedEdge

返回类型 方法 描述
DirectedEdge(int v, int w, double weight)
double weight() 边权重
int from() 边的顶点
int to() 边指向的顶点
String toString() 字符串化
  1. public class DirectedEdge {
  2. private final int v;//起
  3. private final int w;//终
  4. private final double weight;
  5. public DirectedEdge(int v, int w, double weight){
  6. this.v = v;
  7. this.w = w;
  8. this.weight = weight;
  9. }
  10. public double weight(){ return weight;}
  11. public int from(){ return v; }
  12. public int to(){ return w; }
  13. public String toStirng(){
  14. return String.format("%d->%d %.2f", v, w, weight);
  15. }
  16. }

加权有向图API:public class EdgeWeightedDigraph

返回类型 方法 描述
EdgeWeightedDigraph(int V) 创建V个顶点的无边图
EdgeWeightedDigraph(In in) 从标准输入in读入一幅有向图
int V() 顶点数
int E() 边数
void addEdge(DirectedEdge e) 添加边e
Iterable adj(int v) v指向的所有顶点
Iterable edges() 图中所有边
String toString() 对象的字符串表示
  1. import edu.princeton.cs.algs4.In;
  2. public class EdgeWeightedDigraph {
  3. private final int V;//顶点数
  4. private int E;//边总数
  5. private Bag<DirectedEdge>[] adj;//邻接表
  6. public EdgeWeightedDigraph(int V){
  7. this.V = V;
  8. this.E = 0;
  9. adj = (Bag<DirectedEdge>[])new Bag[V];
  10. for (int v = 0; v < V; v++)
  11. adj[v] = new Bag<>();
  12. }
  13. public EdgeWeightedDigraph(In in){
  14. this(in.readInt());
  15. int E = in.readInt();
  16. for (int i = 0; i < E; i++){
  17. int v = in.readInt();
  18. int w = in.readInt();
  19. double weight = in.readDouble();
  20. DirectedEdge e = new DirectedEdge(v,w,weight);
  21. addEdge(e);
  22. }
  23. }
  24. public void addEdge(DirectedEdge e){
  25. adj[e.from()].add(e);
  26. }
  27. public int V(){ return V;}
  28. public int E(){ return E;}
  29. public Iterable<DirectedEdge> adj(int v){ return adj[v];}
  30. public Iterable<DirectedEdge> edges(){
  31. Bag<DirectedEdge> bag = new Bag<>();
  32. for (int v = 0; v < V; v++)
  33. for (DirectedEdge e: adj[v])
  34. bag.add(e);
  35. return bag;
  36. }
  37. }

最短路径API:public class SP

返回类型 方法 描述
SP(EdgeWeightedDigraph G, int s)
double distTo(int v) s->v距离,不存在为∞
boolean hasPathTo(int v) 是否存在s->v
Iterable pathTo(int v) s->v的路径,不存在为null
  • 数据结构选择
    • SPT中的边:顶点索引的DirectedEdge数组edgeTo[]
    • 到起点距离: distTo[]

松弛(relaxation)

  • 边的松弛:放松v->w,即检查s->w是否经s->v->w:若distTo[v]+(v->w)e.weight()w失效,更新edgeTo[w]为由s->w为v->w
  1. private void relax(DirectedEdge e){
  2. int v = e.from(), w = e.to();
  3. if(distTo[w] > distTo[v] + e.weight()){
  4. distTo[w] = distTo[v] + e.weight();
  5. edgeTo[w] = e;
  6. }
  7. }

relaxation.jpg

  • 顶点的松弛:放松从一个顶点指出的所有边
  1. private void relax(EdgeWeightedDigraph G, int v){
  2. for(DirectedEdge e:G.adj(v)){
  3. int w = e.to();
  4. if(distTo[w] > distTo[v] + e.weight()){
  5. distTo[w] = distTo[v] + e.weight();
  6. edgeTo[w] = e;
  7. }
  8. }
  9. }

SP中的查询方法

  1. private double distTo(int v){ return distTo[v]};//s->v的距离
  2. public boolean hasPathTo(int v){ return distTo[v] < Double.POSITIVE_INFINITY;}
  3. public Iterable<DirectedEdge> pathTo(int v){
  4. Stack<DirectedEdge> stack = new Stack<>();
  5. for(DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()])
  6. stack.push(e);
  7. return stack;//后根入栈
  8. }

SP算法理论基础

  • 最短路径的最优性条件

    • 命题:当且仅当从v到w的任意一条边满足distTo[w]<=distTo[v]+e.weight()时(即不存在有效边时),distTo[]中都为SP的长度

      证明:书P420

  • 通用最短路径算法:

    1. 将distTo[s]初始化为0,其他都初始化为∞
    2. 放松G中任意边,知道不存在有效边
    • 对任意s可达的w,操作过后,distTo[w]即为s到w的最短路径长度,且edgeTo[w]为路径上最后一条边
    • 通用算法没有指定放松顺序

4.4.4 Dijkstra算法(算法4.9)

4.4.5 无环加权有向图的最短路径算法(算法4.10)

4.4.6 一般加权有向图的最短路径问题(算法4.11)


4.4.7 总结

  • 不同最短路径算法总结 | 算法 | 局限 | 时间(最坏) | 最好 | 空间 | 优势 | | :—-: | :—-: | :—-: | :—-: | :—-: | :—-: | | Dijkstra即时版本 | 边权必须为正 | ElogV | ElogV | V | 最坏时保证性能 | | 利用Topo排序 | 只适合无环加权有向图 | E+V | E+V | V | 无环图最优算法 | | 基于队列Bellman-Ford | 不能存在负权重环 | E+V | VE | V | 应用广泛 |