数据结构与算法(13):最短路径

最短路径树

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

数据结构

加权有向边的数据类型

在这里插入图片描述

package Graph;

public class DirectedEdge implements Comparable<DirectedEdge>{
    private int v, w;
    private double weight;
    public DirectedEdge(int v, int w, double weight){
        this.v = v;
        this.w = w;
        this.weight = weight;
    }
    public double weight(){
        return weight;
    }
    public int from(){
        return v;
    }
    public int to(){
        return w;
    }
    @Override
    public int compareTo(DirectedEdge o) {
        return (int)Math.floor(weight - o.weight);// 注意浮点数的比较
    }
}

DirectedEdge类的实现比无向边的数据类型Edge类更简单,因为边的两个端点是有区别的。

加权有向图的数据类型

在这里插入图片描述

package Graph;

import java.util.LinkedList;
import java.util.Queue;

public class EdgeWeightDigraph {
    private int V;
    private int E;
    private Bag<DirectedEdge>[] adj;
    public EdgeWeightDigraph(int V){
        adj = new Bag[V];
        for(int i = 0; i < V; i++){
            adj[i] = new Bag<DirectedEdge>();
        }
    }
    public int V(){
        return V;
    }
    public int E(){
        return E;
    }
    public void addEdge(int v, int w, double weight){
        adj[v].add(new DirectedEdge(v, w, weight));
        E++;
    }
    Iterable<DirectedEdge> adj(int v){
        return adj[v];
    }
    public EdgeWeightDigraph reverse(){
        EdgeWeightDigraph copy = new EdgeWeightDigraph(V);
        for(int v = 0; v < V; v++){
            for(DirectedEdge e : adj(v)){
                copy.addEdge(e.from(), e.to(), e.weight());
            }
        }
        return copy;
    }
    public Iterable<DirectedEdge> edges(){
        Queue<DirectedEdge> list = new LinkedList<>();
        boolean[] marked = new boolean[V];
        for(int v = 0; v < V; v++){
            marked[v] = true;
            for(DirectedEdge e: adj(v)){
                int w = e.to();
                if(marked[w]) continue;
                list.add(e);
                marked[w] = true;
            }
        }
        return list;
    }
}

与Digraph类一样,每条边在邻接表中只会出现一次。

最短路径的API

在这里插入图片描述

最短路径的数据结构

  • 最短路径树中的边:使用一个由顶点索引的DirectedEdge对象的父链接数组edgeTo[],其中edgeTo[v]的值为树中连接v和它的父结点的边。
  • 到达起点的距离:我们需要一个由顶点索引的数组distTo[],其中distTo[v]为从s到v的已知最短路径的长度。
    我们约定edgeTo[s]的值为null,distTo[s]的值为0。同时还约定,从起点到不可达顶点的距离为Double.POSITIVE_INFINITY。

边的松弛

distTo[]中只有起点所对应的元素的值为0,其余元素的值均被初始化为Double.POSITIVE_INFINITY。随着算法的执行,它将起点到其他顶点的最短路径的信息存入了edgeTo[]和distTo[]数组中。在遇到新的边时,通过更新这些信息就可以得到新的最短路径。

边的松弛:放松边v->w意味着检查从s到w的最短路径是否是先从s到v,然后再由v到w。如果是,则根据这个情况更新数据结构的内容。

顶点的松弛

实际上,实现会放松从一个给定顶点指出的所有边。

Dijkstra算法

首先将distTo[s]初始化为0,distTo[]中的其他元素初始化为无穷。然后将distTo[]最小的非树顶点放松并加入树中,如此这般,直到所有顶点都在树中或者所有非树顶点的distTo[]值均为无穷大。

package Graph;

import Sort.IndexMinPQ;

import java.util.Stack;

public class DijkstraSP {
    private double[] distTo;
    private DirectedEdge edgeTo[];
    private IndexMinPQ<Double> pq;
    public DijkstraSP(EdgeWeightDigraph G, int s){
        int V = G.V();
        distTo = new double[V];
        for(int v = 0; v < V; v++) distTo[v] = Double.POSITIVE_INFINITY;
        edgeTo = new DirectedEdge[V];

        pq = new IndexMinPQ<>(V);
        distTo[s] = 0.0;
        pq.insert(s, 0.0);
        while (!pq.isEmpty()){
            relax(G, pq.delMin());
        }
    }
    private void relax(EdgeWeightDigraph G, int v){
        for(DirectedEdge e : G.adj(v)){
            int w = e.to();
            if(e.weight() + distTo[v] < distTo[w]){
                distTo[w] = e.weight() + distTo[v];
                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){
        Stack<DirectedEdge> stack = new Stack<DirectedEdge>();
        for(DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()]){
            stack.add(e);
        }
        return stack;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值