写这篇博客的原因——昨天写判断图的连通性代码的时候碰壁了,其实不是写不出满足需求的代码,是对用矩阵的方式求解花了比较长的时间来理解,原因主要是没有把矩阵的运算与图很好的联系起来,后面虽然明白了,但心里一直没放下,今天晚上就在想——如果两点之间的值表示的是两者的距离,那么矩阵把乘号改成加号后的求的值就表示两点经过中间结点后能抵达的距离和,那如果把求和改成求最小值呢?于是我想到了用这个方式求个点之间的最短距离。话不多说开始吧😄
先把昨天判断图的连通性的矩阵公式拿来说道说道:
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+⋯+Mn−1其中,M为图的邻接矩阵,n为顶点数
如果矩阵
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×M∴mij2=k=1∑nmik×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>0且mkj>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=1∑nmik×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=Mk−1×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×A但aij2=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}
An−1表示各点之间的最小距离了。
代码:
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
运行结果: