各点之间的最短距离——Java实现

博客探讨了如何利用矩阵运算求解图的连通性和最短路径问题。首先解释了邻接矩阵在判断图连通性中的应用,然后提出将矩阵乘法改为求最小值,以计算两点间的最短距离。通过修改后的矩阵运算,作者实现了Floyd算法,该算法遍历所有节点以找到任意两点间的最短路径。最后,给出了实现这些算法的Java代码示例并展示了测试结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写这篇博客的原因——昨天写判断图的连通性代码的时候碰壁了,其实不是写不出满足需求的代码,是对用矩阵的方式求解花了比较长的时间来理解,原因主要是没有把矩阵的运算与图很好的联系起来,后面虽然明白了,但心里一直没放下,今天晚上就在想——如果两点之间的值表示的是两者的距离,那么矩阵把乘号改成加号后的求的值就表示两点经过中间结点后能抵达的距离和,那如果把求和改成求最小值呢?于是我想到了用这个方式求个点之间的最短距离。话不多说开始吧😄

先把昨天判断图的连通性的矩阵公式拿来说道说道:
M a = M 0 + M 1 + M 2 + ⋯ + M n − 1 其 中 , M 为 图 的 邻 接 矩 阵 , n 为 顶 点 数 \begin{aligned} &M_a=M^0+M^1+M^2+\cdots+M^{n-1}\\ &其中,M为图的邻接矩阵,n为顶点数 \end{aligned} Ma=M0+M1+M2++Mn1Mn
如果矩阵 M a M_a Ma存在有 m i j = = 0 m_{ij}==0 mij==0则表示该图为非连通图。
理解如下:
矩阵 M 0 M^0 M0表示与 M M M同型的单位阵,表示结点可以自己到达其自身;
矩阵 M 1 M^1 M1也就是我们常说的图的邻接矩阵,是我们直观看到的图转成矩阵存储的形式,一般理解 m i j 1 m^1_{ij} mij1表示顶点 i 能否达到顶点 j ,说得更准确一点—— m i j 1 m^1_{ij} mij1表示顶点 i 能否经过一条边达到顶点 j;
M 2 M^2 M2的值呢?代表什么?
我们先从矩阵乘法入手:
M 2 = M × M ∴ m i j 2 = ∑ k = 1 n m i k × m k j \begin{aligned} &M^2=M\times M \\ &\therefore m^2_{ij}=\sum\limits_{k=1}^n m_{ik}\times m_{kj} \end{aligned} M2=M×Mmij2=k=1nmik×mkj
m i k × m k j m_{ik}\times m_{kj} mik×mkj取出来结合矩阵的值的意义来理解: m i k m_{ik} mik表示结点 i 能否到达结点 k,如果值为 0,则表示不可达, m k j m_{kj} mkj同理,两个值做乘法,如果值大于0,则说明 m i k > 0 且 m k j > 0 m_{ik}>0且m_{kj}>0 mik>0mkj>0,所以 i 能到达 k ,k 能到达 j,根据传递性,表示 i 能到达 j ;如果两者积为0,与上面分析过程相同,表示 i 不能通过顶点 k 到达 j。

再联系求和符号, m i j 2 = ∑ k = 1 n m i k × m k j m^2_{ij}=\sum\limits_{k=1}^n m_{ik}\times m_{kj} mij2=k=1nmik×mkj表示顶点 i 经过任意一个顶点到达顶点 j 的路径方式的和,比如顶点 i 可以经过顶点 a 到达顶点 j 且 顶点 i 还可以经过顶点 b 到达顶点 j ,那么 m i j 2 = 2 m^2_{ij}=2 mij2=2

M k = M k − 1 × M ( k > 1 ) M^k=M^{k-1}\times M(k>1) Mk=Mk1×M(k>1)以此类推就可以明白: m i j k m_{ij}^k mijk表示顶点 i 能否经过k条边到达顶点 j 。
这也就说明为什么 M a M_a Ma能够判断该图是否是连接图。

回到最初问题:
我们用邻接矩阵A来存储图, a i j a_{ij} aij表示结点 i 到结点 j 的距离,如果为 ∞ \infty 则表示不可达。
现在我们把上面的矩阵乘法拿来改一下,依然是乘法的流程但不是乘法操作:
A 2 = A × A 但 a i j 2 = m i n ( a i j , a i k + a k j ) , k = 1 , ⋯   , n , ( k ≠ i , k ≠ j ) \begin{aligned} &A^2=A\times A \\ &但 a^2_{ij}=min( a_{ij},a_{ik}+a_{kj}),k=1,\cdots,n,(k\not =i,k\not =j) \end{aligned} A2=A×Aaij2=min(aij,aik+akj),k=1,,n,k=i,k=j
先理解 a i k + a k j a_{ik}+a_{kj} aik+akj a i k a_{ik} aik表示顶点 i 直接到达顶点 j 的距离, a k j a_{kj} akj同理,根据传递性,两者求和 a i k + a k j a_{ik}+a_{kj} aik+akj表示顶点 i 经过顶点 k 到达顶点 j 的距离。
加上一个最小值判断,自然而然地得到了表示顶点 i 经过任何一个非( i 或 j )的顶点到达顶点 j 的最小距离。
所以, A l A^l Al的值 a i j l a^l_{ij} aijl则表示表示顶点 i 经过 l 条边到达顶点 j 的最小距离。
所以,经过n-1条得到的 A n − 1 A^{n-1} An1表示各点之间的最小距离了。

代码:

package datastruct;

import java.util.Arrays;

public class Graph {

	/**
	 * The data.
	 */
	int[][] data;

	/**
	 * 
	 *********************
	 * The first constructor.
	 * 
	 * @param paraRows    The number of rows.
	 * @param paraColumns The number of columns.
	 *********************
	 *
	 */
	public Graph(int paraRows, int paraColumns) {
		data = new int[paraRows][paraColumns];
	}// Of the first constructor

	/**
	 * 
	 *********************
	 * The second constructor.Construct a copy of given matrix.
	 * 
	 * @param paraMatrix The given matrix.
	 *********************
	 *
	 */
	public Graph(int[][] paraMatrix) {
		data = new int[paraMatrix.length][paraMatrix[0].length];

		// Copy elements
		for (int i = 0; i < data.length; i++) {
			for (int j = 0; j < data[0].length; j++) {
				data[i][j] = paraMatrix[i][j];
			} // Of for j
		} // Of for i
	}// Of the second constructor

	/**
	 * 
	 *********************
	 * @Title: setValue
	 * @Description: TODO(Set on the value of one element.)
	 *
	 * @param paraRow    The row of the element.
	 * @param paraColumn The column of the element.
	 * @param paraValue  The new value.
	 *********************
	 *
	 */
	public void setValue(int paraRow, int paraColumn, int paraValue) {
		data[paraRow][paraColumn] = paraValue;
	}// Of setValue

	/**
	 * Overrides the method claimed in Object,the superclass of any class.
	 */
	public String toString() {
		return Arrays.deepToString(data);
	}// Of toString

	/**
	 * 
	 *********************
	 * @Title: getMinimumDistance
	 * @Description: TODO(Get the minimal distance.)
	 *
	 * @return The result.
	 *********************
	 *
	 */
	public int[][] getMinimalDistance() {
		int numberNodes = data.length;
		int[][] resultData = new int[numberNodes][numberNodes];
		// Step 1. Initialize the resultData.
		for (int i = 0; i < numberNodes; i++) {
			for (int j = 0; j < numberNodes; j++) {
				resultData[i][j] = data[i][j];
			} // Of for i;
		} // Of for i;

		for (int l = 2; l < numberNodes; l++) {
			getData(resultData);
		} // Of for l

		return resultData;
	}// Of getMinimalDistance

	public void getData(int[][] paraData) {
		int numberNodes = data.length;
		int tempMin, tempData;

		for (int i = 0; i < numberNodes; i++) {
			for (int j = 0; j < numberNodes; j++) {
				// To pass the a_ii
				if (i == j)
					continue;

				tempMin = paraData[i][j];
				for (int k = 0; k < numberNodes; k++) {
					// To prevent overflow.
					if (data[i][k] == Integer.MAX_VALUE || data[k][j] == Integer.MAX_VALUE)
						continue;
					tempData = data[i][k] + data[k][j];
					// Get the minimum.
					tempMin = tempMin < tempData ? tempMin : tempData;

				} // Of for k

				// Update the result.
				paraData[i][j] = tempMin;
			} // Of for j
		} // Of for i
	}// Of getData

	/**
	 * 
	 *********************
	 * @Title: main
	 * @Description: TODO(The entrance of the program)
	 *
	 * @param args Not used now
	 *********************
	 *
	 */
	public static void main(String args[]) {

		int intfty = Integer.MAX_VALUE;

		int[][] tempMatrix = { { 0, 6, 13 }, { 10, 0, 4 }, { 5, intfty, 0 } };
		Graph tempGraph = new Graph(tempMatrix);
		System.out.println(tempGraph);

		int[][] resultMatrix = tempGraph.getMinimalDistance();
		System.out.println(Arrays.deepToString(resultMatrix));
	}// Of main
}// Of class Graph

测试结果:
在这里插入图片描述
虽然时间复杂度是大了些,但也算是之前的一个小小拓展吧,mark一下。

要求各点之间的最短距离,自然也会想到floyd算法,这个算法思想也很简单——遍历所有顶点,遍历过程中把该点作为中间结点,看其它结点通过该结点作为中转能否减少两点之间的距离,遍历完所有顶点就可以得出两点最终的最短距离。
代码:

/**
	 * 
	 *********************
	 * @Title: floyd
	 * @Description: TODO(Get the minimal distance between two points.)
	 *
	 * @return The result data.
	 *********************
	 *
	 */
	public int[][] floyd() {
		int tempNodes = data.length;
		int[][] resultData = new int[tempNodes][tempNodes];

		// Initialize the resultData.
		for (int i = 0; i < tempNodes; i++) {
			for (int j = 0; j < tempNodes; j++) {
				resultData[i][j] = data[i][j];
			} // Of for j
		} // Of for i

		int tempData;
		for (int i = 0; i < tempNodes; i++) {
			for (int j = 0; j < tempNodes; j++) {
				for (int k = 0; k < tempNodes; k++) {
					if (resultData[j][i] == Integer.MAX_VALUE || resultData[i][k] == Integer.MAX_VALUE)
						continue;
					tempData = resultData[j][i] + resultData[i][k];
					resultData[j][k] = resultData[j][k] <= tempData ? resultData[j][k] : tempData;
				}
			} // Of for j
		} // Of for i

		return resultData;
	}//Of floyd

运行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值