算法導論——最短路徑: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文本數據的時候,表示該圖是不含負權重環的,打印輸出的結果爲: java

從原點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做爲文本數據的時候,該圖就是一個含有負權重環的加權有向圖,理應當是不含有最短路徑的,那麼咱們看看咱們的程序是否能正確檢測出來呢?

打印結果: 算法

該有向加權圖含有負權重環,不存在最短路徑
相關文章
相關標籤/搜索