最短路径1----Dirkstra算法

加权有向图:有向路径,边的权重

单点最短路径:从s到t路径中权重最小者

最短路径树SPT:起始点s到所有可达顶点的最短路径

                                                                 Shortest path

加权有向图数据结构

加权有向边

//加权有向图边数据结构
public class DirectedEdge {
	
	private final int v;
	private final int w;
	private final 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;
	}
	
	public String toString(){
		return String.format("%d->%d %.2f", v, w, weight);
	}

}

对于边s-->t  from返回s   to返回t,分别获取某一条边的起始点和终点。

加权有向图数据结构

//加权有向图
public class EdgeWeightedDigraph {
	
	private final int V;
	private int E;
	private Bag<DirectedEdge>[] adj;
	
	public EdgeWeightedDigraph(int V){
		this.V = V;
		this.E = 0;
		adj = new Bag[V];
		for(int v=0; v<V; v++){
			adj[v] = new Bag<DirectedEdge>();
		}
	}
	
	public EdgeWeightedDigraph(int V, int E, int[] vr, int[] wr, double[] ww){
		this(V);
		for(int i=0; i<E; i++){
			DirectedEdge e = new DirectedEdge(vr[i], wr[i], ww[i]);
			addEdge(e);
		}
	}
	
	public int V(){
		return this.V;
	}
	
	public int E(){
		return this.E;
	}
	
	public void addEdge(DirectedEdge e){
		int f = e.from();
		adj[f].add(e);
		E++;
	}
	
	public Iterable<DirectedEdge> adj(int v){
		return adj[v];
	}
	
	public Iterable<DirectedEdge> edges(){
		Bag<DirectedEdge> bag = new Bag<>();
		for(int v=0; v<this.V; v++){
			for(DirectedEdge e : adj[v]){		
				bag.add(e);
			}
		}
		return bag;
	}
	
	public String toString(){
		String s = V + " vertices, " + E + " edges\n";
		for(int v = 0; v < V; v++){
			s += v + ": ";
			Iterator<DirectedEdge> it = this.adj(v).iterator();
			while(it.hasNext()){
				DirectedEdge e = it.next();
				if(it.hasNext())
					s += e.toString() + " ---> ";
				else
					s += e.toString();
			}
			s += "\n";
		}
		return s;
	}
}

对于邻接表数组来说,每个数组元素是一个有向边结构的背包链表,由于边是有向的,因此只需要在from初始顶点背包中添加一条边即可;edges方法返回有向加权的所有有向边。

示例如下所示:

         Shortest pathedge-weighted digraph representation

松弛操作

给定起始点,求出起始点s到所有可达顶点的最短路径,即求出最短路径树SPT。

利用edgeTo数组来保存最短路径树的边,edgeTo[v]=e表示的含义是从起始点s到达v的最短路径上最后一条边为e。

利用distTo数组来保存到达起始点的距离,例如distTo[v]=x,表示从s到v已知最短路径长度为x。

规定edgeTo[s]=null  distTo[s]=0  distTo[不可达顶点]=无穷大。

边的松弛

假定在某一个时刻起始点s到顶点v的最短距离为distTo[v],从起始点s到顶点w的已知最短路径为distTo[w],而s-->w的最短路径上最后一条边为edgeTo[w];

顶点w为顶点v的相邻顶点,边e:v-->w连接v与w。由于s可以到达v,通过v可以访问到w,因此从s到达w的可能路径增加一条s---v---w,如果这条路径权重比已知的路径要小,则从s到达w的状态可以更新。

private void relax(DirectedEdge e) {
    int v = e.from(), w = e.to();
    if (distTo[w] > distTo[v] + e.weight()) {
        distTo[w] = distTo[v] + e.weight();
        edgeTo[w] = e;
    }
}

顶点松弛

将顶点v所有可达顶点全部进行松弛操作,则完成顶点v的松弛

   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;
			}
		}
	}

                     edge relaxation

最短路径的最优性条件

给定加权有向图G,起始点s,distTo数组。对于从s可达的所有顶点v,distTo[v]表示从s到v的某条路径,对于不可达顶点,该值为无穷大;当且仅当对于从v到w的任意边e,满足distTo[w]<=distTo[v]+e.weight,它们是最短路径的长度。

通用最短路径算法

distTo[s]初始化0,其余值初始化无穷大,放松G中的任意边,直到不存在有效边为止。此时distTo[w]即为从s到w的最短路径长度,且edgeTo[w]=e表示该最短路径的最后一条边为e。

Dijkstra算法----权重非负的加权有向图最短路径

从通用最短路径算法出发,distTo[s]初始化0,其余值初始化无穷大。不断将distTo[]中最小的非树顶点放松并加入树中,直到所有顶点都在树中,或者所有非树顶点值都为无穷大。

利用edgeTo来保存最短路径的最后一条边,distTo表示最短路径长度,优先级队列来排序distTo数组来不断取出最小值。

代码实现如下:

//权重非负的加权有向图单起点最短路径问题
public class DijkstraSP {
	
	//最短路径路中连接v和其父节点的边  edgeTo[v]=e表示 s---->v最短路径上最后一条边为e
	private DirectedEdge[] edgeTo;
	//起始点到某个顶点已知最短路径的长度
	private double[] distTo;
	//顶点序号,距离起始点距离  二元组的优先级队列,每次都处理距离起始点最近的顶点
	private PriorityQueue<VertexDist> pq;
	
	public DijkstraSP(EdgeWeightedDigraph g, int s){
		edgeTo = new DirectedEdge[g.V()];
		distTo = new double[g.V()];
		pq = new PriorityQueue<>();
		for(int v=0; v<g.V(); v++){
			distTo[v] = Double.POSITIVE_INFINITY;
		}
		distTo[s] = 0.0;
		pq.add(new VertexDist(s, 0.0));
		//每次都处理距离起始点最近的顶点
		//由于新顶点v加入,假定起始点s到新顶点v的某一个相邻顶点w存在某一条路径
		//由于v的加入此时出现一条新路径s--v---w,该路径可能比之前某条s--w路径短,如果短则更新distTo[w]以及edgeTo[w]
		//判断条件是distTo[w] > distTo[v]+e.weight()
		while(!pq.isEmpty()){
			relax(g, pq.poll());
		}
	}

	private void relax(EdgeWeightedDigraph g, VertexDist vd) {
		int v = vd.getV();
		//顶点v的松弛
		for(DirectedEdge e : g.adj(v)){
			int w = e.to();
			//边e的松弛
			if(distTo[w] > distTo[v]+e.weight()){
				VertexDist t = new VertexDist(w, distTo[w]);
				edgeTo[w] = e;
				distTo[w] = distTo[v]+e.weight();
				if(pq.contains(t)){
					pq.remove(t);
					t.setW(distTo[w]);
					pq.add(t);
				}else{		
					pq.add(new VertexDist(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;
		Stack<DirectedEdge> s = new Stack<>();
		for(DirectedEdge e=edgeTo[v]; e!=null; e=edgeTo[e.from()]){
			s.push(e);
		}
		List<DirectedEdge> ll = new ArrayList<>();
		while(!s.isEmpty()){
			ll.add(s.pop());
		}
		return ll;
	}

}

空间与V正比,时间与ElogV正比。

通用最短路径算法并没有指明放松顶点的顺序,Dijkstra算法每次添加的都是离起点最近的非树顶点。其局限是加权有向图的边权重非负。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值