算法导论——最短路径:BellmanFord算法

package org.loda.graph;

import org.loda.structure.Stack;
import org.loda.util.In;

/**
 * 
 * @ClassName: BellmanFord
 * @Description: 最短路径问题
 * 
 * 通用最短路径算法,能解决除了含负权重环以外的所有情况的最短路径,包括了含有负权重边的有向加权图
 * 
 * @author minjun
 * @date 2015年5月28日 下午11:04:45
 * 
 */
public class BellmanFord {

	// 原点
	private int s;

	// 从原点到达i的距离为dist[i]
	private double dist[];

	// 最短路径的前驱节点
	private int[] prev;

	// 是否含有负权重环,默认false表示不含有
	private boolean negativeCycle;

	public BellmanFord(WeightDigraph g, int s) {

		int v = g.v();
		this.s = s;

		dist = new double[v];
		prev = new int[v];

		for (int i = 0; i < v; i++) {
			dist[i] = Double.POSITIVE_INFINITY;
			prev[i] = -1;
		}

		/**
		 * 由于最短路径一定是一条不含有环的简单路径 1.如果有正环,一定不是最短路径 2.如果是0环,可以去掉改环多余部分也不影响
		 * 3.如果是负环,则表示不存在最短路径
		 * 
		 * 简单路径中,数量为V的元素个数形成的最短路径有为V-1个边,对所有边进行V-1次更新,会得到所有点的最短路径
		 */
		// 不含有负环,所以s->s的最短路径当然为0
		dist[s] = 0;

		for (int i = 0; i < v - 1; i++) {
			for (int m = 0; m < v; m++) {
				relax(m, g);
			}
		}

		/**
		 * 在完成了v-1次全部边更新之后,理应无法继续更新,因为已经找到了s距离每个点的最短路径,如果还能继续更新,说明该图中含有负环。
		 * 一旦该图含有负环,则表示可以无限次更新下去,也就是说该图根本不存在最短路径
		 */

		for (int m = 0; m < v; m++) {
			for (Edge edge : g.adj(m)) {
				int n = edge.otherSide(m);

				if (dist[n] > dist[m] + edge.weight()) {
					// 含有负权重环
					negativeCycle = true;
					return;
				}
			}
		}

	}

	private void relax(int m, WeightDigraph g) {
		for (Edge edge : g.adj(m)) {
			int n = edge.otherSide(m);

			if (dist[n] > dist[m] + edge.weight()) {
				dist[n] = dist[m] + edge.weight();
				prev[n] = m;
			}
		}
	}

	/**
	 * 
	 * @Title: distTo
	 * @Description: s->d的最短距离
	 * @param @param d
	 * @param @return 设定文件
	 * @return double 返回类型
	 * @throws
	 */
	public double distTo(int d) {
		return dist[d];
	}

	/**
	 * 
	 * @Title: hasPathTo
	 * @Description: s->d是否存在可达路径
	 * @param @param d
	 * @param @return 设定文件
	 * @return boolean 返回类型
	 * @throws
	 */
	public boolean hasPathTo(int d) {
		return distTo(d) < Double.POSITIVE_INFINITY;
	}

	/**
	 * 
	 * @Title: pathTo
	 * @Description: s->d的最短路径
	 * @param @param d
	 * @param @return 设定文件
	 * @return Iterable<Integer> 返回类型
	 * @throws
	 */
	public Iterable<Integer> pathTo(int d) {
		if (!hasPathTo(d))
			return null;
		Stack<Integer> path = new Stack<Integer>();

		for (int i = d; i != -1; i = prev[i]) {
			path.push(i);
		}

		return path;
	}

	public static void main(String[] args) {
		//不含负权重环的文本数据
		String text1="F:\\算法\\attach\\tinyEWDn.txt";
		//含有负权重环的文本数据
		String text2="F:\\算法\\attach\\tinyEWDnc.txt";
		WeightDigraph g = new WeightDigraph(new In(text1
				));
		BellmanFord d = new BellmanFord(g, 0);

		if (d.negativeCycle) {
			System.out.println("该有向加权图含有负权重环,不存在最短路径");
		} else {
			for (int i = 0; i < g.v(); i++) {
				Iterable<Integer> path = d.pathTo(i);
				if (path == null) {
					System.out.println("从原点" + d.s + "到" + i + "没有可达路径");
				} else {
					System.out.println("从原点" + d.s + "到" + i + "的最短距离为:"
							+ d.distTo(i));
					System.out.print("路径为:");
					for (int j : d.pathTo(i)) {
						System.out.print(j + "->");
					}
					System.out.println();
				}
			}
		}
	}
}

上述代码采用text1文本数据的时候,表示该图是不含负权重环的,打印输出的结果为:

从原点0到0的最短距离为:0.0
路径为:0->
从原点0到1的最短距离为:0.9300000000000002
路径为:0->2->7->3->6->4->5->1->
从原点0到2的最短距离为:0.26
路径为:0->2->
从原点0到3的最短距离为:0.9900000000000001
路径为:0->2->7->3->
从原点0到4的最短距离为:0.26000000000000023
路径为:0->2->7->3->6->4->
从原点0到5的最短距离为:0.6100000000000002
路径为:0->2->7->3->6->4->5->
从原点0到6的最短距离为:1.5100000000000002
路径为:0->2->7->3->6->
从原点0到7的最短距离为:0.6000000000000001
路径为:0->2->7->



当上述测试代码切换到使用text2作为文本数据的时候,该图就是一个含有负权重环的加权有向图,理应当是不含有最短路径的,那么我们看看我们的程序是否能正确检测出来呢?

打印结果:

该有向加权图含有负权重环,不存在最短路径

你可能感兴趣的:(算法导论,最短路径,BellmanFord,负权重)