1. 程式人生 > >圖的最短路徑演算法——Dijkstra演算法的 java 實現

圖的最短路徑演算法——Dijkstra演算法的 java 實現

首先,定義Graph類,主要用於儲存圖的鄰接矩陣,實際上儲存的是每個節點的出邊(outgoing arcs)集合。

Graph 類繼承自 SparseMatrix 類,因為大多數圖(網路)都是稀疏的,所以用稀疏矩陣來儲存圖的邊及每條邊的權值非常方便。

package matrix;

import java.util.Map;

import matrix.SparseMatrix;

public class Graph extends SparseMatrix {
	int nodeNum; // 圖的節點數
	boolean symmetric; // 是否為無向圖
	public static
final double INF = Double.MAX_VALUE; // 表示不相鄰的節點之間的直接距離為無窮大 public Graph(int n, boolean s) { super(n, n); // n x n 的稀疏矩陣 symmetric = s; nodeNum = n; setDefaultValue(INF); // 將稀疏矩陣的預設值設成 無窮大 } public int getNodeNum() { return nodeNum; } public void addEdge(int i, int j) { try { put(
i, j, 1); if (symmetric) { put(j, i, 1); } } catch (IndexException | TypeException e) { e.printStackTrace(); } } public void addEdge(int i, int j, double c) { try { put(i, j, c); if (symmetric) { put(j, i, c); } } catch (SparseMatrix.IndexException | TypeException e)
{ e.printStackTrace(); } } public void addEdges(double[][] triples) { for (int i = 0; i < triples.length; ++i) { addEdge((int) triples[i][0], (int) triples[i][1], triples[i][2]); } } public double getEdge(int i, int j) { if (i == j) { return 0; } try { return (double) get(i, j); } catch (IndexException e) { e.printStackTrace(); return INF; } } // 獲取節點 i 的出邊集 public Map<Integer, Object> getOutEdges(int i) { return rows.get(i); } // 展示圖的鄰接矩陣 public void show() { for (int i = 0; i < nodeNum; ++i) { for (int j = 0; j < nodeNum; ++j) { String c = getEdge(i, j) < INF ? String.valueOf(getEdge(i, j)) : "inf"; System.out.print(c + " "); } System.out.println(); } } }

下面來看圖的求最短路徑演算法——Dijkstra演算法



public class ShortestPath {

	int origin;
	int[] preNodes;
	double[] distances;
	boolean hasNegativeLoop;

	public ShortestPath(int o, double[] dis, int[] pre, boolean has) {
		origin = o;
		preNodes = pre;
		distances = dis;
		hasNegativeLoop = has;
	}

	// 返回 o 到 d 的最短路程
	static double Dijkstra(Graph g, int origin, int destination) {
		return Dijkstra(g, origin).distances[destination];
	}

	// 返回 o 到所有節點的最短路程
	static ShortestPath Dijkstra(Graph g, int origin) {
		int nodeNum = g.getNodeNum();

		// 初始化前向節點
		int pre[] = new int[nodeNum];
		pre[origin] = origin;

		// 初始化距離向量
		double[] distances = new double[nodeNum];
		for (int i = 0; i < nodeNum; ++i) {
			distances[i] = Graph.INF;
		}
		distances[origin] = 0;

		// 初始化候選頂點集,即為起始節點
		Set<Integer> V = new HashSet<Integer>();
		V.add(Integer.valueOf(origin));

		while (!V.isEmpty()) {

			// 選出 V 中距離起點最近的節點,從V中刪除
			Integer toRemove = null;

			double minDisInV = Graph.INF;
			for (Integer i : V) {
				if (distances[i] <= minDisInV) {
					minDisInV = distances[i];
					toRemove = i;
				}
			}
			V.remove(toRemove);

			// 在與toRemove的出邊相連的節點中,選擇節點 j
			Map<Integer, Object> outEdges = g.getOutEdges(toRemove);
			if (outEdges != null) {
				for (Integer j : outEdges.keySet()) {
					// 如果節點 j 可以使得距離向量更小,把 j 加入 V
					if (distances[j] > distances[toRemove] + (double) outEdges.get(j)) {
						distances[j] = distances[toRemove] + (double) outEdges.get(j);
						pre[j] = toRemove;
						if (!V.contains(j)) {
							V.add(j);
						}
					}
				}
			}
		}

		return new ShortestPath(origin, distances, pre, false);
	}


	public void analyse() {
		System.out.println("Negative loops exist: " + hasNegativeLoop);
		for (int i = 0; i < distances.length; ++i) {
			String c = distances[i] < Graph.INF ? String.valueOf(distances[i]) : "inf";
			System.out.println("Distance from " + origin + " to " + i + ": " + c);
			if (distances[i] < Graph.INF) {
				int j = i;
				while (j != origin) {
					System.out.print(j + " <= ");
					j = preNodes[j];
				}
				System.out.println(j);
			}
		}
	}



}
	

讓我們來看一個示例:
在這裡插入圖片描述

	public static void main(String[] args) {
		Graph g = new Graph(6,false);
		double[][] triples = {
				{1,2,2},
				{1,3,1},
				{2,3,1},
				{3,2,1},
				{2,4,1},
				{3,4,3},
				{2,5,0},
				{4,5,0}
				};
		g.addEdges(triples);
		g.show();

		ShortestPath sp = ShortestPath.Dijkstra(g, 1);

		System.out.println();
		sp.analyse();

	}

其結果為:

0.0 inf inf inf inf inf 
inf 0.0 2.0 1.0 inf inf 
inf inf 0.0 1.0 1.0 0.0 
inf inf 1.0 0.0 3.0 inf 
inf inf inf inf 0.0 0.0 
inf inf inf inf inf 0.0 

Negative loops exist: false
Distance from 1 to 0: inf
Distance from 1 to 1: 0.0
1
Distance from 1 to 2: 2.0
2 <= 1
Distance from 1 to 3: 1.0
3 <= 1
Distance from 1 to 4: 3.0
4 <= 2 <= 1
Distance from 1 to 5: 2.0
5 <= 2 <= 1

原本圖中有5個節點,但因為輸入三元組的時候,節點的起始下標為1,所以定義了6個節點(包含一個孤立的節點0),從圖的權值矩陣也可以看出,節點0到其他節點的距離為無窮大。但它的存在不影響我們計算節點1到其他節點的最短路程:

d(1,1) = 0.0
d(1,2) = 2.0
d(1,3) = 1.0
d(1,4) = 3.0
d(1,5) = 2.0