图的最短路径算法——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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值