Dijkstra(Java)

Dijkstra算法(Java实现)

核心思想

从一开始先确定一个最短路径,然后不断去更新该路径,最终得到起始节点到其余节点的最短路径。

代码

public class Dijkstra {
	private static final int MAX_DISTANCE = 1000; // 无限距离,用于初始化距离
	
	public static void main(String[] args) {
		int max = MAX_DISTANCE;
		int[][] graph = {
				{0, 10, max, 4, max, max},
				{10, 0, 8, 2, 6, max},
				{max, 8, 10, 15, 1, 5},
				{4, 2,  15, 0, 6, max},
				{max, 6, 1, 6, 0, 12},
				{max, max, 5, max, 12, 0}};
		
		System.out.println("输入起始节点:");
		@SuppressWarnings("resource")
		Scanner in = new Scanner(System.in);
		
		int startNode = Integer.parseInt(in.nextLine());
		int[] dist = dijkstra(graph, startNode);
		System.out.println(Arrays.toString(dist));
	}
	
	/**
	 * Dijkstra算法,其调用了findMinDistNode方法
	 * @param graph 进行查找的图,这里用二维矩阵实现
	 * @param startNode 起始节点
	 * @return 返回起始节点到其他节点的最短路径距离列表
	 */
	public static int[] dijkstra(int[][] graph, int startNode) {
		char[] vertexList = {'A', 'B', 'C', 'D', 'E', 'F'};
		
		String[] findProcess = new String[6]; // 用于打印最短路径的字符串数组
		findProcess[0] = String.valueOf(vertexList[startNode]); // 直接将开始节点赋给findProcess[0]
		int[] processDist = new int[6]; // 用于打印最短路径距离的整型数组(和findProcess搭配打印)
		processDist[0] = 0;
		
		int vertexNumbers = graph.length;
		int[] dist = new int[vertexNumbers]; // 当下startNode到每个顶点的最短距离
		Arrays.fill(dist, MAX_DISTANCE); // 将初始距离都设为最大值
		dist[startNode] = 0; // startNode和自己的距离为0
		boolean[] visited = new boolean[vertexNumbers]; // 该列表标记顶点是否被放入最终最短路径中
		
		// 第一个for循环用于找到minNode,将其加入最终的最短路径
		for (int i = 0; i < vertexNumbers; i++) {
			int minNode = findMinDistNode(dist, visited); // 未被访问且startNode能达到的最短距离的顶点
			visited[minNode] = true; // 将此顶点加入已访问列表,保证之后循环findMinDistNode方法时不再访问该顶点,因为已经确定将该点加入最终最短路径了

			if (i == 0) ; // 开始节点到开始节点不进行任何操作
			else {
			    findProcess[i] = findProcess[i-1] + " -> " + vertexList[minNode]; // 开始节点到每次确定下的minNode之间的路径				
			}
			processDist[i] = dist[minNode]; // 开始节点到每次确定下的minNode之间的距离
			
			// 第二个for循环以minNode顶点为新的开始点进行后续节点的查找,保证从startNode到后续节点的距离为当下最小距离
			for (int j = 0; j < vertexNumbers; j++) {
				/*
				 *  minNode和后续节点j满足几个条件:
				 *  1、后续节点j未被访问过
				 *  2、在图中minNode和后续节点距离不为0,即不是同一个节点
				 *  3、minNode和startNode距离不为最大值
				 *  4、startNode到minNode的最短距离(dist[minNode])加minNode和后续节点的距离小于startNode直接到后续节点的最短距离(dist[j])
				 */
				if (!visited[j] && graph[minNode][j] != 0 && dist[minNode] != MAX_DISTANCE 
						&& dist[minNode] + graph[minNode][j] < dist[j]) {
					dist[j] = dist[minNode] + graph[minNode][j]; // 更新dist列表,保证dist[j]是目前为止的最小值
				}
			}	
		}
		System.out.println("起始节点到各节点的最短路径及距离如下:");
		for (int i = 1; i < 6; i++) {
			System.out.println(findProcess[i] + " 的最短距离为 " + processDist[i]);
		}
		return dist;
	}
	
	// 从dist[]中其余未访问的节点中找到startNode能到达的距离最小的顶点
	public static int findMinDistNode(int[] dist, boolean[] visited) {
		int minDist = MAX_DISTANCE;
		int minNode = -1;
		for (int i = 0; i < dist.length; i++) {
			// 若距离为MAX_DISTANCE,则说明startNode无法到达该顶点
			if (!visited[i] && dist[i] < minDist) {
				minDist = dist[i]; // 更新minDist,保证最后得出的是可达到的距离最短的顶点
				minNode = i; // 更新minNode
			}
		}
		return minNode;
	}
}

重要变量说明

变量说明
startNode起始节点,一旦被赋值就不会变
graph进行查找的图,用二维矩阵实现
dist[]保存起始节点到其他节点的最短距离,该数组元素会不断更新,这里的起始节点是控制台输入的最初的起始节点,之后不会改变
vertexList[]顶点数组,对应dist[]
findProcess[]用于打印最短路径的字符串数组
processList[]用于打印最短路径距离的整型数组(和findProcess搭配打印)
visited[]该列表标记顶点是否被放入最终最短路径中
minNode当下startNode能达到的最短距离的顶点,会加入到最终dist[]中

手动推导Dijkstra算法过程(以初始节点为A点为例)

1、确定startNodevertexList[0] 即A点;
2、找到A能到达的点,并将距离记录在dist[] 中;
3、选择除了A点之外dist[] 中最小距离所对应的点作为minNode,此表中为D点;
4、计算A通过D到其他各点的距离,更新dist[] ,确保各元素值为当下的最小值,此时D点已加入最短路径,标记为已访问;
5、重复上述3、4操作,得到最终的dist[] 数组,即A点到各点的最短路径距离。
具体过程如下图:
左侧黑色的表代表dist[],红色圈出来的代表已加入最终最短路径的节点minNode,右侧红色的过程代表以minNode为中介访问其他节点的过程
手动推导Dijkstra算法过程

代码执行结果如下:

输入起始节点:
0
起始节点到各节点的最短路径及距离如下:
A -> D 的最短距离为 4
A -> D -> B 的最短距离为 6
A -> D -> B -> E 的最短距离为 10
A -> D -> B -> E -> C 的最短距离为 11
A -> D -> B -> E -> C -> F 的最短距离为 16
[0, 6, 11, 4, 10, 16]

参考文章:
[1] https://www.cnblogs.com/goldsunshine/p/12978305.html
[2] https://developer.aliyun.com/article/1181921

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值