zoukankan      html  css  js  c++  java
  • 单源最短路径 djkstra

    代码:

    public class Djkstra {
    
    /*
    单源最短路径
    
    时间复杂度 O(ElogV) ,主要取决于优先队列的实现
    空间复杂度 O(V)
    
    djkstr 和普通的 广度优先非常相似,唯一多考虑了一点:边有不同的权重(不再一直是1了)
    基于普通广度优先思想,到达某个顶点的最短距离 = 到达这个顶点要经历的边的个数
    djkstr的目的和普通广度优先算法一样,希望对周围能到达的顶点,再最早的时刻对其进行访问(得到访问该顶点的最小成本)
    那么问题是,加上边的权重之后,我们该如何将问题化为普通广度优先的思考方式呢?
    
    一个办法是:
    我们可以把一条加权的边想象成包含了匿名中间节点的数条边组成
    例如
    有一张图
    
    a-(4)->b
     -(3)-^
    
    一共有2条从a到b的边
    
    权重为 3的边 从a点到b点可以看成
    a-0-0-b (0为匿名顶点)
    同时
    a到b点还有另外一条边,权重是4,可以看成这样
    a-0-0-0-b
    
    那么完整的图为
    
    a-0-0-0-b
     -0-0--^
    
    此时按照普通广度优先的思想,从a点出发,能先走到b点的一定是到达b点的最小成本,是哪条边呢?
    是权重小的那条边
    
    所以我们在普通广度优先算法的基础上,将一个普通的队列,替换成一个优先队列
    每当遇到周围可探索顶点的时候,我们计算一下,从当前顶点的成本 + 到达待探索顶点的边的成本 = 从当前点探索该点的成本
    然后将待探索顶点和探索该点的成本放入优先队列(若到达该点的路径有多个,那么我们始终在优先队列中保留成本低的那个)
    下次从优先队列中取出的顶点,就是我们该探索的下一个顶点,也就是能最先到达,成本最低的那个顶点
    
    题外:
    类似的思想,在加权无向图的最小生成树的Prim即时版中也有使用(优先队列),只不过它不累加探索成本到待探索点上,而是直接
    以探索某个点的最短路径的成本作为优先队列的排序条件(这样能确保下一个探索的点,是通过当前已知的成本最低的边走过去的(一种贪心思想))
    
    另外还有一个常用的最短路径算法:贝尔曼福特,
    
    * */
    
        EdgeWeightedGraph ewg;
        IndexMinPQ<Float> minPQ;
        float[] cost;         //到达顶点v的成本
        Edge[] fromEdge;      //记录顶点V是从哪条边探索过来的,除了顶点,每个顶点有一条
        int s;
    
        public Djkstra(EdgeWeightedGraph ewg, int s) {
            this.ewg = ewg;
            this.s = s;
            minPQ = new IndexMinPQ<>(ewg.v());
            cost = new float[ewg.v()];
            fromEdge = new Edge[ewg.v()];
            for (int i = 0; i < cost.length; i++)
                cost[i] = Integer.MAX_VALUE;
            dj();
            this.ewg = null;
        }
    
        private void dj() {
            minPQ.insert(s + 1, 0f);
            while (!minPQ.isEmpty()) {
                int v = minPQ.topIndex() - 1;         //当前要探索的顶点
                float vcost = minPQ.delTop();         //到达当前探索顶点的总成本
                cost[v] = vcost;
    
                for (Edge e : ewg.adj(v)) {            //将周围顶点入队
                    int w = e.other(v);                //当前顶点的相邻顶点
                    if (cost[w] != Integer.MAX_VALUE)  //已探索过的不再次探索
                        continue;
                    if (minPQ.contain(w + 1)) {     //若已经存在于优先队列中,保留探索总成本小的
                        if (minPQ.get(w + 1) > cost[v] + e.weight()) {
                            minPQ.change(w + 1, cost[w] + e.weight());
                            fromEdge[w] = e;
                        }
                    } else {
                        minPQ.insert(w + 1, cost[v] + e.weight());
                        fromEdge[w] = e;                //第一次探索到该顶点
                    }
                }
            }
        }
    
        //从起点到v点的总成本
        public float toVCost(int v) {
            return cost[v];
        }
    
        //从起点,是从哪条边到达v点的
        public Edge toVEdge(int v) {
            return fromEdge[v];
        }
    
        //点到v点的路径
        public Stack<Edge> toVEdges(int v) {
            Stack<Edge> edges = new Stack<>();
            Edge e = fromEdge[v];
            int toV = v;
            while (e != null) {
                edges.push(e);
                toV = e.other(toV);     //到达当前点的来源点
                e = fromEdge[toV];
            }
            return edges;
        }
    
        public static void main(String[] args) {
            /*
             *        (2)
             *     1 ---- 3
             * (2)/|     |
             *   / | (3) |
             * 0   |     |(2)
             *    |(1)  |
             * (1)|     |
             *     2 ---- 4
             *        (4)
             * */
    
            EdgeWeightedGraph ewg = new EdgeWeightedGraph(5);
            ewg.addEdge(0, 1, 2);
            ewg.addEdge(0, 2, 1);
            ewg.addEdge(1, 2, 1);
            ewg.addEdge(1, 3, 2);
            ewg.addEdge(1, 4, 3);
            ewg.addEdge(2, 4, 4);
            ewg.addEdge(3, 4, 2);
            System.out.println(ewg);
    
            int s = 0;
            int w = 4;
            Djkstra dj = new Djkstra(ewg, s);
            System.out.println("from " + s + " to " + w + "'s path: ");
            Stack<Edge> edges = dj.toVEdges(w);
            while (!edges.empty()) {
                Edge e = edges.pop();
                System.out.print(e + " , ");
            }
            System.out.println();
            System.out.println("total cost : " + dj.toVCost(w));
        }
    }

    输出

    0: 0-1 2.00, 0-2 1.00, 
    1: 0-1 2.00, 1-2 1.00, 1-3 2.00, 1-4 3.00, 
    2: 0-2 1.00, 1-2 1.00, 2-4 4.00, 
    3: 1-3 2.00, 3-4 2.00, 
    4: 1-4 3.00, 2-4 4.00, 3-4 2.00, 
    
    from 0 to 4's path: 
    0-2 1.00 , 2-4 4.00 , 
    total cost : 5.0
  • 相关阅读:
    活动设计的“七宗罪”(转)
    BAYESIAN STATISTICS AND CLINICAL TRIAL CONCLUSIONS: WHY THE OPTIMSE STUDY SHOULD BE CONSIDERED POSITIVE(转)
    iOS开发—— UIImagePickerController获取相册和拍照
    iOS开发——UIImageView
    iOS开发——导入第三方库引起的unknown type name 'NSString'
    iOS开发——UITableView(未完,待续...)
    iOS开发——Reachability和AFNetworking判断网络连接状态
    iOS开发——GCDAsyncSocket
    iOS开发——pch文件创建
    iOS开发——打开手机相册,获取图片
  • 原文地址:https://www.cnblogs.com/cyy12/p/12041189.html
Copyright © 2011-2022 走看看