单源最短路径 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

原文地址:https://www.cnblogs.com/cyy12/p/12041189.html

时间: 2024-10-22 01:18:26

单源最短路径 djkstra的相关文章

Dijkstra算法求单源最短路径

1.最短路径 在一个连通图中,从一个顶点到另一个顶点间可能存在多条路径,而每条路径的边数并不一定相同.如果是一个带权图,那么路径长度为路径上各边的权值的总和.两个顶点间路径长度最短的那条路径称为两个顶点间的最短路径,其路径长度称为最短路径长度. 最短路径在实际中有重要的应用价值.如用顶点表示城市,边表示两城市之间的道路,边上的权值表示两城市之间的距离.那么城市A到城市B连通的情况下,哪条路径距离最短呢,这样的问题可以归结为最短路径问题. 求最短路径常见的算法有Dijkstra算法和Floyd算法

单源最短路径 dijkstra算法实现

本文记录一下dijkstra算法的实现,图用邻接矩阵表示,假设图为无向图,并且连通,有向图,不连通图的做法类似. 算法简述: 首先确定"单源"的源,假设是第0个顶点. 维护三个数组dist[], color[], path[],设其下标分别为0-i-n-1: dist[] 表示源点到顶点i的最短距离,在初始化时,如果源点到顶点i有路径,则初始化为路径的权重,否则初始化为INT_MAX: color[] 数组其实表示两个集合,即color[i]值为1的集合表示已经确定最短路径的点的集合,

数据结构之单源最短路径(迪杰斯特拉算法)-(九)

最开始接触最短路径是在数据结构中图的那个章节中.运用到实际中就是我在大三参加的一次美赛中,解决中国的水资源问题.所谓单源最短路径,就是一个起点到图中其他节点的最短路径,这是一个贪心算法. 迪杰斯特拉算法原理(百科): 按路径长度递增次序产生算法: 把顶点集合V分成两组: (1)S:已求出的顶点的集合(初始时只含有源点V0) (2)V-S=T:尚未确定的顶点集合 将T中顶点按递增的次序加入到S中,保证: (1)从源点V0到S中其他各顶点的长度都不大于从V0到T中任何顶点的最短路径长度 (2)每个顶

数据结构:单源最短路径--Dijkstra算法

Dijkstra算法 单源最短路径 给定一带权图,图中每条边的权值是非负的,代表着两顶点之间的距离.指定图中的一顶点为源点,找出源点到其它顶点的最短路径和其长度的问题,即是单源最短路径问题. Dijkstra算法 求解单源最短路径问题的常用方法是Dijkstra(迪杰斯特拉)算法.该算法使用的是贪心策略:每次都找出剩余顶点中与源点距离最近的一个顶点. 算法思想 带权图G=<V,E>,令S为已确定了最短路径顶点的集合,则可用V-S表示剩余未确定最短路径顶点的集合.假设V0是源点,则初始 S={V

dijkstra 两点的最短路径 单源 最短路径

思路以dist数组 来扩充  路径的访问,不断的刷新dist数组 设置一个顶点的集合s,并不断地扩充这个集合,一个顶点属于集合s当且仅当从源点到该点的路径已求出.开始时s中仅有源点,并且调整非s中点的最短路径长度,找当前最短路径点,将其加入到集合s,直到终点在s中.基本步骤:1.把所有结点分成两组:      第一组:包括已经确定最短路径的结点:      第二组:包括尚未确定最短路径的结点.2.开始时,第一组只包含起点,第二组包含剩余的点:3.用贪心的策略,按最短路径长度递增的顺序把第二组的结

单源最短路径 Bellman_ford 和 dijkstra

首先两个算法都是常用于 求单源最短路径 关键部分就在于松弛操作 实际上就是dp的感觉 if (dist[e.to] > dist[v] + e.cost) { dist[e.to] = dist[v] + e.cost; ... } bellman_ford O(E*V) 但是配合队列可以 有spfa 可以达到O(kE) http://www.360doc.com/content/13/1208/22/14357424_335569176.shtml 并且bellman_ford还适用于负边 并

数据结构与算法--单源最短路径算法之dijkstra

单源最短路径之dijkstra算法 最优子问题:dis(s,...,e)是s到e的最短路径,在这条路径上的所有点之间dis(pi,pj)距离是最小的. 算法思路: 首先初始化,dis[s][i]是s到i的距离,直接相连的就是其距离,不直接相连的就是无穷大 下面是算法主要模块: 1.选取dis[i]最小的点加入到P{S}中, 2.计算是否更新dis[j],j是和i直接相连的 3.重复以上步骤,直到e

单源最短路径、所有结点对的最短路径

算法核心:两个结点之间的一条最短路径包含着(包含于)其它的最短路径.[最短路径性质] 1.单源最短路径Dijkstra 思路:计算每个结点到源结点的距离,压入最小优先队列Q,对Q中的元素进行如下循环操作: 1.从队列Q中弹出最小元素u 2.将u并入S 3.对u的邻接表中每个结点v,调用Relax(u,v,w)更新结点v到源结点s的距离 直至Q为空. 伪代码: Initialize-Single-Source(G,s) for each vertex v in G.v v.d = MAX v.p

单源最短路径算法---Dijkstra

Dijkstra算法树解决有向图G=(V,E)上带权的单源最短路径问题,但是要求所有边的权值非负. 解题思路: V表示有向图的所有顶点集合,S表示那么一些顶点结合,从源点s到该集合中的顶点的最终最短路径的权值(程序中用dist[i]表示)已经确定.算法反复选择具有最短路径估计的顶点u 属于 V-S(即未确定最短路径的点,程序中finish[i]=false的点),并将u加入到S中(用finish[i]=true表示),最后对u的所有输出边进行松弛. 程序实现:      输入数据: 5 7 0