最短路径3---Bellman-Ford算法

通用最短路径算法

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

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

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

Dijkstra算法每次处理距离起始点距离最近的非树顶点,并且要求加权有向图的边权重非负。下面给出无环加权有向图最短路径一般解法。

Acyclic算法----无环加权有向图最短路径算法
distTo[s]初始化0,其余值初始化无穷大。按照拓扑排序顺序来放松所有顶点,可以在E+V正比时间内解决无环加权有向图最短路径。

下面给出一般加权有向图的最短路径算法

负权重环:环上所有边权重和为负的有向环

从s到v的有向路径上的任意顶点都不存在于负权重环上时,s到v的最短路径才存在。

负权重环的检测问题

Bellman-Ford算法----一般加权有向图最短路径算法

给定G以及s,从s无法到达任何负权重环。distTo[s]初始化0,其余值初始化无穷大,以任意顺序放松图中所有边,重复V轮,可以解决最短路径问题。

代码如下,时间复杂度O(EV)

for (int pass = 0; pass < G.V(); pass++)
   for (int v = 0; v < G.V(); v++)
      for (DirectedEdge e : G.adj(v))
          relax(e);

基于队列的Bellman-Ford算法

只有上一轮中distTo[]值发生变化的顶点指出的边才能改变其他的distTo[]元素的值。

利用queue来保存正在被放松的顶点,利用onQ来标识某个顶点是否在队列中。将起始点s加入到队列中,并标记s。不断遍历队列取出的顶点元素v,进行顶点v放松,如果v的某个相邻顶点w发生松弛,则将w加入到队列中。利用cost来记录relax执行的次数,每当完成一轮处理时,此时edgeTo中保存最短路径树SPT,构建SPT,判断是否含有负权重环。

bellman-ford算法实现:

//一般加权有向图的最短路径
public class BellmanFordSP {
	
	//起点到某个顶点的最短路径长度
	private double[] distTo;
	//从起点到某个顶点最短路径最后一条边
	private DirectedEdge[] edgeTo;
	//是否在队列中
	private boolean[] onQ;
	//正在处理的顶点队列
	private Queue<Integer> queue;
	//调用relax此时
	private int cost;
	//负权重环
	private Iterable<DirectedEdge> cycle;
	
	public BellmanFordSP(EdgeWeightedDigraph g, int s){
		distTo = new double[g.V()];
		edgeTo = new DirectedEdge[g.V()];
		onQ = new boolean[g.V()];
		queue = new LinkedList<>();
		for(int v=0; v<g.V(); v++){
			distTo[v] = Double.POSITIVE_INFINITY;
		}
		distTo[s] = 0.0;
		queue.add(s);
		onQ[s] = true;
		while(!queue.isEmpty() && !hasNegativeCycle()){
			int v = queue.poll();
			onQ[v] = false;
			relax(g, v);
			//System.out.println(queue);
		}
	}

	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(!onQ[w]){
					queue.add(w);
					onQ[w] = true;
				}
			}
			if(cost++%g.V()==0){
				findNegativeCycle();
			}
		}
	}

	//利用edgeTo构建最短路径树SPT,寻找负权重环
	private void findNegativeCycle() {
		int V = edgeTo.length;
		EdgeWeightedDigraph spt = new EdgeWeightedDigraph(V);
		for(int v=0; v<V; v++){
			if(edgeTo[v]!=null){
				spt.addEdge(edgeTo[v]);
			}
		}
		//System.out.println(cost + "  " + spt.toString());
		DirectedCycle dc = new DirectedCycle(spt);
		cycle = dc.cycle();
	}
	
	public Iterable<DirectedEdge> negativeCycle(){
		return cycle;
	}

	public boolean hasNegativeCycle() {
		return cycle != null;
	}
	
	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;
	}
}

负权重环的检测实现DFS

//加权有向图  负权重环检测   dfs
public class DirectedCycle {
	
	//记录是否访问过
	private boolean[] marked;
	//有向路径保存
	private DirectedEdge[] edgeTo;
	//记录是否在当前栈中
	private boolean[] onStack;
	//记录最终的环节点
	private Stack<DirectedEdge> cycle;
	
	public DirectedCycle(EdgeWeightedDigraph g){
		marked = new boolean[g.V()];
		onStack = new boolean[g.V()];
		edgeTo = new DirectedEdge[g.V()];
		for(int s=0; s<g.V(); s++){
			if(!marked[s])
				dfs(g, s);
		}
	}
	
	private void dfs(EdgeWeightedDigraph g, int v) {
		//标记当前节点入栈
		onStack[v] = true;
		//标记已经访问
		marked[v] = true;
		for(DirectedEdge e : g.adj(v)){
			if(hasCycle()) return;
			//如果w没有被访问过  记录v-->w路径  对于w继续进行dfs  w入栈
			int w = e.to();
			if(!marked[w]){
				edgeTo[w] = e;
				dfs(g, w);
			}
			//如果w已经出现在栈中,说明出现了环
			//出现有向环   记录该有向环路径
			else if(onStack[w]){
				cycle = new Stack<DirectedEdge>();
				DirectedEdge f = e;
                while (f.from() != w) {          
                    cycle.push(f);
                    f = edgeTo[f.from()];
                }
                cycle.push(f);
                return;
			}
		}
		//w出栈
		onStack[v] = false;
	}

	public boolean hasCycle(){
		return cycle != null;
	}
	
	public Iterable<DirectedEdge> cycle(){
		return this.cycle;
	}
}

 

上一篇:web php wrong nginx config


下一篇:把测试错误的图像重新挑选出来进行测试