图的最短路径算法——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);
			}
		}
	}



}
	

让我们来看一个示例:
《图的最短路径算法——Dijkstra算法的 java 实现》

	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
    原文作者:Dijkstra算法
    原文地址: https://blog.csdn.net/itnerd/article/details/83041065
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞