问:如何计算出Floyd最短路径算法中的路径链?
程序猿答疑:
最近看了网上一些Floyd最短路径算法的实现,在求出最短路径的距离矩阵和路径矩阵后如果需要具体列出某两点之间的路径都是用路径矩阵动态打打印的,没有保存到具体的数据结构中。今天程序员答疑给大家演示下如何用Matlab语言实现Floyd最短路径算法并计算路径链,路径链保存在列表中,在Matlab中的形式为一个1*n的矩阵。
Floyd算法
如上图,6个点A1、A2、A3、A4、A5、A6,点与点之间连线上的数字表示两点间的距离,如A1到A2的距离为5,点与点之间没有直接连线表示两点之间不能直达,可通过其他点作为中间点形成通路,如A1到A3没有直达路径,但从A1出发可以先到A2,再从A2到A3,这条路径的长度为9(A1到时A2距离为5,A2到A3距离为4,两者之和为9)。我们把6个点每2个点之间的连线距离填写在一个邻接矩阵a中,矩阵的i行j列上的数字a[i][j]为A[i]点与A[j]点的直接连线距离。如果两点之间没有直接连线,直达距离为无穷大,用inf表示(inf在Matlab中表示无穷大),同一点与自身的距离为0。上图中的a矩阵为
a=[
0 5 inf 8 6 6;
5 0 4 9 inf inf;
inf 4 0 inf inf 10;
8 9 inf 0 1 inf;
6 inf inf 1 0 3;
6 inf 10 inf 3 0]
为叙述方便,程序员答疑直接用Matlab中的矩阵形式表示矩阵(带分号“;”)。我们可以看到,邻接矩阵是一个对称矩阵,即第i行第j列上的数字与第j行第i列的数字相同,即a[i][j]=a[j][i],这是因为A[i]点与A[j]点的连线距离就是A[j]点与A[i]点的连线距离;同时可以观察到邻接矩阵上的主对角线上的数字a[i][i]为0,即A[i]点到A[i]点的距离记为0(在一个二维的数字矩阵中,从左上角至右下角的对角线为主对角线,从右上角至左下角的对角线为次对角线)。
Floyd算法的输出为最短距离矩阵d和路由矩阵p,最短距离矩阵d的第i行第j列数值d[i][j]表示A[i]点到A[j]点的最短路径长度,路由矩阵p的第i行第j列数值p[i][j](假设为k)表示A[i]点到A[j]点的最短路径由A[i]点到A[k]点的连线和A[k]点到A[j]点的最短路径组合而成,如果k=j,A[k]点到A[j]的最短路径长度为0。最短距离矩阵d初始设置为与邻接矩阵a相同,路由矩阵p初始设置为每列上的数值与列序号相同。两个矩阵初始值的含义为两个点的最短距离初始化为两点的直接连线长度,最短路径为两点间的直接连线(若无直接连线仍认为距离为无穷大)。比如p[2][3]的值为3,即A2点到A3点的最短路径由A2点到A3点的连线和A3点到A3点的最短路径组合而成,A3点到A3点的最短路径长度为0,即在初始路由矩阵p中,A2点到A3点的最短路径为A2到A3的连线,用A2->A3表示,最短路径长度为4。当然,这和最终结果可能不同,比如初始路由矩阵p中A1点到A4点的最短路径为A1->A4,最短路径长度为8,而实际情况是A1点到A4点的最短路径为A1->A5->A4表示,最短路径长度为7。
d=[
0 5 inf 8 6 6;
5 0 4 9 inf inf;
inf 4 0 inf inf 10;
8 9 inf 0 1 inf;
6 inf inf 1 0 3;
6 inf 10 inf 3 0]
p=[
1 2 3 4 5 6;
1 2 3 4 5 6;
1 2 3 4 5 6;
1 2 3 4 5 6;
1 2 3 4 5 6;
1 2 3 4 5 6]
Floyd算法的思想是对于任意两点A[i]和A[j],如果存在一个点A[k]使得A[i]和A[k]的最短路径长度d[i][k]与A[i]和A[j]的最短路径长度d[k][j]之和小于当前A[i]和[j]和最短路径之和d[i][j],则更新d[i][j]为d[i][k]+d[k][j],并设置p[i][j]=p[i][k],即把路径A[i]到A[j]的A[i]的下一个点由p[i][j]换为路径A[i]到A[k]的A[i]的下一个点p[i][k],具体算法如下(Matlab代码源文件名为floyd.m):
function [d,p]=floyd(a)
n=size(a,1); %测出a矩阵的行数
d=a; %初始化距离矩阵
for i=1:n %初始化路由矩阵
for j=1:n
p(i,j)=j; %使路由矩阵p形成初始矩阵(每一列上的数为该列的列序数)
end
end
for k=1:n %开始循环遍历计算(用if语句来比较通过k点中转前后的大小)
for i=1:n
for j=1:n
if d(i,k)+d(k,j)<d(i,j) %需要更新的条件(也就是中转后比原来要小)
d(i,j)=d(i,k)+d(k,j);
p(i,j)=p(i,k);
end
end
end
end
对于图中6个点的距离数据应用Floyd算法得到
d =
0 5 9 7 6 6
5 0 4 9 10 11
9 4 0 13 13 10
7 9 13 0 1 4
6 10 13 1 0 3
6 11 10 4 3 0
p =
1 2 2 5 5 6
1 2 3 4 4 1
2 2 3 2 6 6
5 2 2 4 5 5
1 4 6 4 5 6
1 1 3 5 5 6
矩阵d和p是打印在Matlab命令行窗口的形式,与代码中的矩阵表现形式稍有不同(不带分号“;”)。
路径链
根据矩阵d和p可以得出图中任意两点间的最短路径和最短路径的长度。例如A1到A2的最短路径长度为d[1][2]=5,p[1][2]=2,即A1到A2的最短路径要从A1先到A2,A2已经是终点了,不再继续查找路径,最短路径为A1->A2。又如A2到A6的最短路径长度为d[2][6]=11,p[2][6]=1,即A2到A6的最短路径要从A2先到A1,A2->A1,再看p[1][6]的值为6,A6已经是终点了,不再继续查找路径,最短路径为A1->A2->A6。
上述查找最短路径的方法包含着递归的思想,程序猿答疑用递归算法计算路径链(Matlab代码源文件名为path.m)。
% 计算点i到点j的最短路径链
% path_list 最短路径链列表
function [path_list]=path(p,i,j,path_list)
% 将i添加到路径链列表path_list
path_list=[path_list i];
if i==j
% fprintf('i==j')
% i和j相等,查找到路径最后一个点,不再继续查找
else
% fprintf('i~=j')
% i和j相等,递归查找点p(i,j)和j之间路径链
path_list=path(p,p(i,j),j,path_list);
end
打印出图中任意两点间的最短路径链(Matlab代码源文件名为main.m)。
a=[
0 5 inf 8 6 6;
5 0 4 9 inf inf;
inf 4 0 inf inf 10;
8 9 inf 0 1 inf;
6 inf inf 1 0 3;
6 inf 10 inf 3 0]
[d,p]=floyd(a)
% path_list最短路径链列表,初始化为空数组[]
path_list=[];
path_list=path(p,i,j,path_list)
n=size(a,1); %测出a矩阵的行数,即图中点个数
for i=1:n
for j=1:n
path_list=[];
path_list=path(p,i,j,path_list);
fprintf('A%d到A%d的最短路径为:',i,j)
[r,c]=size(path_list);
for k=1:c % 打印路径链
if k<c
fprintf('%d->',path_list(k))
else
fprintf('%d\n',path_list(k))
end
end
end
end
代码输出结果为:
A1到A1的最短路径为:1
A1到A2的最短路径为:1->2
A1到A3的最短路径为:1->2->3
A1到A4的最短路径为:1->5->4
A1到A5的最短路径为:1->5
A1到A6的最短路径为:1->6
A2到A1的最短路径为:2->1
A2到A2的最短路径为:2
A2到A3的最短路径为:2->3
A2到A4的最短路径为:2->4
A2到A5的最短路径为:2->4->5
A2到A6的最短路径为:2->1->6
A3到A1的最短路径为:3->2->1
A3到A2的最短路径为:3->2
A3到A3的最短路径为:3
A3到A4的最短路径为:3->2->4
A3到A5的最短路径为:3->6->5
A3到A6的最短路径为:3->6
A4到A1的最短路径为:4->5->1
A4到A2的最短路径为:4->2
A4到A3的最短路径为:4->2->3
A4到A4的最短路径为:4
A4到A5的最短路径为:4->5
A4到A6的最短路径为:4->5->6
A5到A1的最短路径为:5->1
A5到A2的最短路径为:5->4->2
A5到A3的最短路径为:5->6->3
A5到A4的最短路径为:5->4
A5到A5的最短路径为:5
A5到A6的最短路径为:5->6
A6到A1的最短路径为:6->1
A6到A2的最短路径为:6->1->2
A6到A3的最短路径为:6->3
A6到A4的最短路径为:6->5->4
A6到A5的最短路径为:6->5
A6到A6的最短路径为:6