最短路径(加权有向图)

最短路径

1. 最短路径定义以及性质

定义:

在一副加权有向图中,从顶点s到顶点t的最短路径是所有从顶点s到顶点t的路径中总权重最小的那条路径。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XxlaIWOh-1630141074677)(images/image83.png)]

性质:

  1. 路径具有方向性。
  2. 权重不一定等于距离。权重可以是距离、时间、花费等内容,权重最小指的是成本最低。
  3. 只考虑连通图。一副图中并不是所有的顶点都是可达的,如果s和t不可达,那么它们之间也就不存在最短路径,所以只考虑连通图。
  4. 最短路径不一定是唯一的。从一个顶点到达另外一个顶点的权重最小的路径可能会有很多条,所以只需要找出一条即可。

最短路径树:

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

2. 最短路径树API设计

计算最短路径树的经典算法是dijstra算法

类名DijkstraSP
构造方法public DijkstraSP(EdgeWeightedDigraph G, int s):根据一副加权有向图G和顶点s,创建一个计算顶点为s的最短路径树对象
成员方法1.private void relax(EdgeWeightedDigraph G, int v):松弛图G中的顶点v
2.public double distTo(int v):获取从顶点s到顶点v的最短路径的总权重
3.public boolean hasPathTo(int v):判断从顶点s到顶点v是否可达
4.public Queue pathTo(int v):查询从起点s到顶点v的最短路径中所有的边
成员变量1.private DirectedEdge[] edgeTo: 索引代表顶点,值表示从顶点s到当前顶点的最短路径上的最后一条边(也就是最短路径中的连接上一个顶点的边)
2.private double[] distTo: 索引代表顶点,值从顶点s到当前顶点的最短路径的总权重
3.private IndexMinPriorityQueue pq:存放树中顶点与非树中顶点之间的有效横切边

3. 松弛技术

松弛这个词来源于生活:一条橡皮筋沿着两个顶点的某条路径紧紧展开,如果这两个顶点之间的路径不止一条,还有存在更短的路径,那么把皮筋转移到更短的路径上,皮筋就可以放松了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D7ksaxOp-1630141074679)(images/image84.png)]

松弛这种简单的原理刚好可以用来计算最短路径树。

在API中,需要用到两个成员变量edgeTo和distTo,分别存储边和权重。一开始给定一幅图G和顶点s,我们只知道图的边以及这些边的权重,其他的一无所知,此时初始化顶点s到顶点s的最短路径的总权重disTo[s]=0;顶点s到其他顶点的总权重默认为无穷大,随着算法的执行,不断的使用松弛技术处理图的边和顶点,并按一定的条件更新edgeTo和distTo中的数据,最终就可以得到最短路劲树。

边的松弛:

顶点的松弛是基于边的松弛完成的,只需要把某个顶点指出的所有边松弛,那么该顶点就松弛完毕。例如要松弛顶点v,只需要遍历v的邻接表,把每一条边都松弛,那么顶点v就松弛了。

如果把起点设置为顶点0,那么找出起点0到顶点6的最短路径0->2->7>3->6的过程如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lPdCLKZk-1630141074680)(images/image85.png)]

4.最短路径(Dijstra算法)实现

Disjstra算法的实现和Prim算法很类似,构造最短路径树的每一步都是向这棵树中添加一条新的边,而这条新的边是有效横切边pq队列中的权重最小的边

/**
 * 最短路径(dijkstra算法)
 */
public class DijkstraSP {
    // 索引代表顶点,值表示从顶点s到当前顶点的最短路径上的最后一条边
    private DirectedEdge[] edgeTo;
    // 索引代表顶点,值从顶点s到当前顶点的最短路径的总权重
    private double[] distTo;
    // 存放树中顶点与非树中顶点之间的有效横切边
    private IndexMinPriorityQueue<Double> pq;

    // 根据一副加权有向图G和顶点s,创建一个计算顶点为s的最短路径树对象
    public DijkstraSP(EdgeWeightedDigraph G, int s) {
        // 初始化edgeTo
        this.edgeTo = new DirectedEdge[G.V()];
        // 初始化distTo,并且初始化数组中的内容为无穷大,无穷大即表示不存在这样的边
        this.distTo = new double[G.V()];
        for (int i = 0; i < distTo.length; i++) {
            distTo[i] = Double.POSITIVE_INFINITY;
        }
        // 初始化有效横切边队列
        this.pq = new IndexMinPriorityQueue<Double>(G.V());

        // 默认让顶点s进入队列,但s为最短路径树的根结点,所以初始化数组distTo为0.0
        distTo[s] = 0.0;
        pq.insert(s, 0.0);
        // 遍历有效横切边队列
        while (!pq.isEmpty()) {
            // 松弛图G中的顶点
            relax(G, pq.delMin());
        }
    }

    // 松弛图G中的顶点v
    private void relax(EdgeWeightedDigraph G, int v) {
        // 松弛顶点v就是松弛顶点v邻接表中的每一条边,遍历邻接表
        for (DirectedEdge e : G.adj(v)) {
            // 获取边e的终点
            int w = e.to();
            // 判断起点s到w的总权重与s经过v到w的总权重,如果大于则修正数据
            if (distTo[w] > distTo[v] + e.weight()) {
                // 修改s->w的路径
                edgeTo[w] = e;
                // 修改s到w的总权重
                distTo[w] = distTo[v] + e.weight();
                // 如果顶点w已经存在于优先队列pq中,则重置顶点w的权重
                if (pq.contains(w)) {
                    pq.changeItem(w, distTo[w]);
                } else {
                    // 如果顶点w没有出现在优先队列pq中,则把顶点w及其权重加入到pq中
                    pq.insert(w, distTo[w]);
                }
            }
        }
    }

    // 获取从顶点s到顶点v的最短路径的总权重
    public double distTo(int v) {
        return distTo[v];
    }

    // 判断从顶点s到顶点v是否可达
    public boolean hasPathTo(int v) {
        return distTo[v] < Double.POSITIVE_INFINITY;
    }

    // 查询从起点s到顶点v的最短路径中所有的边
    public Queue<DirectedEdge> pathTo(int v) {
        // 如果顶点s到v不可达,则返回null
        if (!hasPathTo(v)) {
            return null;
        }
        // 创建队列Queue保存最短路径的边
        Queue<DirectedEdge> edges = new Queue<>();
        // 从顶点v开始,逆向寻找,一直找到顶点s为止,而起点s为最短路劲树的根结点,所以 edgeTo[s]=null;
        DirectedEdge e = null;
        while (true) {
            e = edgeTo[v];
            if (e == null) {
                break;
            }
            edges.enqueue(e);
            v = e.from();
        }
        return edges;
    }
}
  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

农村小白i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值