【alg4-最短路径】Dijkstra算法

单点最短路径

给定一幅加权有向图和一个起点s,回答“从s到给定的目的顶点v是否存在一条有向路径?如果有,找出最短(总权重最小)的那条路径。”等类似问题。

最短路径树

给定一幅加权有向图和一个顶点s,以s为起点的一棵最短路径树是图的一幅子图,它包含s和从s可达的所有顶点。这颗有向树的根结点为s,树的每条路径都是有向图中的一条最短路径。

最短路径算法的理论基础

最优性条件

令G为一幅加权有向图,顶点s是G中的起点,distTo[]是一个由顶点索引的数组,保存的是G中路径的长度。对于从s可达的所有顶点v,distTo[v]的值是从s到v的某条路径的长度,对于从s不可达的所有顶点v,该值为无穷大。当且仅当对于从v到w的任意一条边e,这些值都满足distTo[w]<=distTo[v]+e.weight()时(换句话说,不存在有效边时),它们是最短路径的长度。

通用算法

将distTo[s]初始化为0,其他distTo[]元素初始化为无穷大,继续如下操作:放松G中的任意边,直到不存在有效边为止。
对于任意从s可达的顶点w,在进行这些操作之后,distTo[w]的值即为从s到w的最短路径的长度。

最短路径的API

API功能
DijkstraSP(EdgeWeightedDigraph G, int s)构造函数
double distTo(int v)从顶点s到v的距离,如果不存在则路径为无穷大
boolean hasPathTo(int v)是否存在从顶点s到v的路径
Iterable<DirectedEdge> pathTo(int v)从顶点s到v的路径,如果不存在则为null

Dijkstra算法

Dijkstra算法能够解决边权重非负的加权有向图的单起点最短路径问题。
Dijkstra算法首先将distTo[s]初始化为0,distTo[]中的其他元素初始化为正无穷。然后将distTo[]最小的非树顶点放松并加入树中,如此这般,直到所有的顶点都在树中或者所有的非树顶点的distTo[]值均为无穷大。
Prim算法每次添加的都是离树最近的非树顶点,Dijkstra算法每次添加的都是离起点最近的非树顶点。

代码

其中索引优先队列为IndexMinPQ
DijkstraSP.java

package section4_4;

import java.util.ArrayList;
import java.util.List;

public class DijkstraSP {

    private DirectedEdge[] edgeTo;
    private double[] distTo;
    private IndexMinPQ<Double> pq;

    public DijkstraSP(EdgeWeightedDigraph G, int s) {
        edgeTo = new DirectedEdge[G.V()];
        distTo = new double[G.V()];
        pq = new IndexMinPQ<>(G.V());

        for (int v = 0;v < G.V();v++) {
            distTo[v] = Double.POSITIVE_INFINITY;
        }
        distTo[s] = 0.0;
        pq.insert(s,0.0);
        while (!pq.isEmpty()) {
            relax(G,pq.delMin());
        }
    }

    private void relax(EdgeWeightedDigraph G, int v) {
        for (DirectedEdge e : G.adj(v)) {
            int w = e.to();
            if (distTo[w] > distTo[v] + e.weight()) {
                distTo[w] = distTo[v] + e.weight();
                edgeTo[w] = e;
                if (pq.contains(w)) {
                    pq.change(w,distTo[w]);
                } else {
                    pq.insert(w,distTo[w]);
                }
            }
        }
    }

    public double distTo(int v) {
        return distTo[v];
    }

    public boolean hasPathTo(int v) {
        return distTo[v] < Double.POSITIVE_INFINITY;
    }

    public Iterable<DirectedEdge> pathTo(int v) {
        if (!hasPathTo(v)) return null;
        List<DirectedEdge> path = new ArrayList<>();
        for (DirectedEdge e = edgeTo[v];e != null;e = edgeTo[e.from()]) {
            path.add(0,e);
        }
        return path;
    }

    public static void main(String[] args) {
        int[][] data = {
                {4,5},
                {5,4},
                {4,7},
                {5,7},
                {7,5},
                {5,1},
                {0,4},
                {0,2},
                {7,3},
                {1,3},
                {2,7},
                {6,2},
                {3,6},
                {6,0},
                {6,4}
        };
        double[] weights = {
                0.35,
                0.35,
                0.37,
                0.28,
                0.28,
                0.32,
                0.38,
                0.26,
                0.39,
                0.29,
                0.34,
                0.40,
                0.52,
                0.58,
                0.93
        };
        int vn = 8;
        int en = 15;
        EdgeWeightedDigraph G = new EdgeWeightedDigraph(vn,en,data,weights);
        int s = 0;
        DijkstraSP sp = new DijkstraSP(G,s);
        for (int t = 0;t < G.V();t++) {
            System.out.print(s + " to " + t);
            System.out.printf(" (%4.2f): ",sp.distTo(t));
            if (sp.hasPathTo(t)) {
                for (DirectedEdge e : sp.pathTo(t)) {
                    System.out.print(e + " ");
                }
                System.out.println();
            }
        }
    }

}

输出:
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值