单源最短路径 djkstra

Posted cyy12

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单源最短路径 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

以上是关于单源最短路径 djkstra的主要内容,如果未能解决你的问题,请参考以下文章

最短路径算法

贪心算法—单源最短路径

单源最短路径Dijkstra算法的思想详细步骤代码

图文解析 Dijkstra单源最短路径算法

单源最短路径

单源最短路径