|
| 1 | +package cn.navyd.lib.algs.graph; |
| 2 | + |
| 3 | +import cn.navyd.lib.algs.util.In; |
| 4 | +import cn.navyd.lib.algs.util.ArrayStack; |
| 5 | + |
| 6 | +/** |
| 7 | + * 无环加权有向图的最短路径算法:<br> |
| 8 | + * 思想:按照拓扑排序的顺序放松每个顶点。因为拓扑顺序的顶点w除了之前的顶点v是不会有顶点指向w的, |
| 9 | + * 顶点w除了树中的路径v指向,没有树外的顶点指向边了,一旦顶点w加入最短路径中,路径不会再变化。<br> |
| 10 | + * 复杂度:所需时间为e+v |
| 11 | + * @author Navy D |
| 12 | + * @date 20170912172917 |
| 13 | + */ |
| 14 | +public class AcyclicSP implements ShortestPath { |
| 15 | + // 最短路径的存储数组 |
| 16 | + private DirectedEdge[] edgeTo; |
| 17 | + // 最短路径权重edgeTo[v] = s->v 最短路径权值 |
| 18 | + private double[] distTo; |
| 19 | + |
| 20 | + /** |
| 21 | + * 在一个无环有向图计算从起点s开始的最短路径 |
| 22 | + * @param g |
| 23 | + * @param s |
| 24 | + */ |
| 25 | + public AcyclicSP(EdgeWeightedDigraph g, int s) { |
| 26 | + edgeTo = new DirectedEdge[g.getV()]; |
| 27 | + distTo = new double[g.getV()]; |
| 28 | + |
| 29 | + validateVertex(s); |
| 30 | + // 将所有顶点距离初始化最大 |
| 31 | + for (int v = 0; v < g.getV(); v++) |
| 32 | + distTo[v] = Double.POSITIVE_INFINITY; |
| 33 | + // 初始化起点s |
| 34 | + distTo[s] = 0.0; |
| 35 | + // 使用拓扑排序的顶点顺序作为取到顶点权重最小的边 |
| 36 | + Topological top = new TopologicalDFS(g); |
| 37 | + // 如果图g含有环 |
| 38 | + if (!top.hasOrder()) |
| 39 | + throw new IllegalArgumentException("Digraph is not acyclic."); |
| 40 | + for (int v : top.order()) |
| 41 | + // 放松每个顶点 |
| 42 | + relax(g, v); |
| 43 | + } |
| 44 | + |
| 45 | + /** |
| 46 | + * 顶点的松弛: |
| 47 | + * 对于给定顶点的所有边,检查每条边v-w的最短路径是否是当前路径s-v-w最短,如果是则更新路径 |
| 48 | + * @param g |
| 49 | + * @param v |
| 50 | + * @author Navy D |
| 51 | + * @date 20170912172541 |
| 52 | + */ |
| 53 | + private void relax(EdgeWeightedDigraph g, int v) { |
| 54 | + for (DirectedEdge e : g.adj(v)) { |
| 55 | + int w = e.to(); |
| 56 | + // 如果发现加上当前顶点边v-w比之前树中边连接距离要短就更新 |
| 57 | + if (distTo[w] > distTo[v] + e.weight()) { |
| 58 | + distTo[w] = distTo[v] + e.weight(); |
| 59 | + edgeTo[w] = e; |
| 60 | + } |
| 61 | + } |
| 62 | + } |
| 63 | + |
| 64 | + /** |
| 65 | + * 返回从起点s到顶点v的最短距离 |
| 66 | + * @param v |
| 67 | + * @return |
| 68 | + * @author Navy D |
| 69 | + * @date 20170917113226 |
| 70 | + */ |
| 71 | + public double distTo(int v) { |
| 72 | + validateVertex(v); |
| 73 | + return distTo[v]; |
| 74 | + } |
| 75 | + |
| 76 | + /** |
| 77 | + * 如果图中存在从s到达顶点v的最短路径就返回true |
| 78 | + * @param v |
| 79 | + * @return |
| 80 | + * @author Navy D |
| 81 | + * @date 20170917113307 |
| 82 | + */ |
| 83 | + public boolean hasPathTo(int v) { |
| 84 | + validateVertex(v); |
| 85 | + return distTo[v] < Double.POSITIVE_INFINITY; |
| 86 | + |
| 87 | + } |
| 88 | + |
| 89 | + /** |
| 90 | + * 返回起点s到顶点v的最短路径集合,如果不存在这个路径就返回null |
| 91 | + * @param v |
| 92 | + * @return |
| 93 | + * @author Navy D |
| 94 | + * @date 20170917113852 |
| 95 | + */ |
| 96 | + public Iterable<DirectedEdge> pathTo(int v) { |
| 97 | + validateVertex(v); |
| 98 | + if (!hasPathTo(v)) |
| 99 | + return null; |
| 100 | + ArrayStack<DirectedEdge> path = new ArrayStack<>(); |
| 101 | + for (DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()]) { |
| 102 | + path.push(e); |
| 103 | + } |
| 104 | + return path; |
| 105 | + } |
| 106 | + |
| 107 | + private void validateVertex(int v) { |
| 108 | + if (v < 0 || v >= distTo.length) |
| 109 | + throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (distTo.length-1)); |
| 110 | + } |
| 111 | + |
| 112 | + |
| 113 | + public static void main(String[] args) { |
| 114 | + EdgeWeightedDigraph g = new EdgeWeightedDigraph(new In("./tinyEWDAG.txt")); |
| 115 | + int s = 1; |
| 116 | + AcyclicSP sp = new AcyclicSP(g, s); |
| 117 | + for (int v = 0; v < g.getV(); v++) { |
| 118 | + if (sp.hasPathTo(v)) { |
| 119 | + System.out.printf("%d to %d (%.2f) ", s, v, sp.distTo(v)); |
| 120 | + for (DirectedEdge e : sp.pathTo(v)) { |
| 121 | + System.out.print(e + " "); |
| 122 | + } |
| 123 | + System.out.println(); |
| 124 | + } |
| 125 | + else { |
| 126 | + System.out.printf("%d to %d no path\n", s, v); |
| 127 | + } |
| 128 | + } |
| 129 | + } |
| 130 | +} |
0 commit comments