最短路径Floyd算法图解与C++实现

邻接矩阵特征 

Floyd算法建立在对图的邻接矩阵的操作上,理解Floyd首先要理解邻接矩阵

对于有向图,邻接矩阵的一行代表该顶点的出边,一列代表该顶点的入边

对于无向图则不区分出边与入边,邻接矩阵表示成一个对称矩阵

graph[i][j] 代表 i -> j ,由 i 直接到 j 的代价,graph[i][k] + graph[k][j]就表示 (i -> k) + (k -> j) ,即 i -> k -> j ,由 i 经 k 到 j 的代价

Floyd代码中有三层for循环,其中最内层for就是在操作这样一个值 + 一行值

下面以上图为例,分析Floyd算法运行过程


 Floyd算法

Floyd算法中使用一个三维数组 ans[k][i][j],表示可以经过的中间结点序号小于等于 k 时,顶点 i 到顶点 j 的最小代价

① ans[0][i][j]的值即邻接矩阵

② ans[1][i][j]的值即,可以经过顶点 1 ,各顶点对间最小代价,这个值是在ans[0][i][j]的基础上得到的。

    经过顶点 1 有什么好处?

    以前只有 i -> j 的边能用,现在 i -> 1 -> j 的边也能用了,表现在邻接矩阵上就是,以前的代价是graph[i][j],现在可以是

graph[i][1]  + graph[1][j]了,即在这个三维矩阵中ans[1][i][j]的值应该等于min{ans[0][i][j], ans[0][i][1] + ans[0][1][j]},经过这样的比较,就将顶点 1 能产生的影响考虑了进去。

③ 同上,将所有顶点都考虑进去,就得到了整个图中顶点对的最短路径


代码实现与分析

代码中实际使用的是一个二维数组ans[i][j],下面先给出代码,然后在解释代码中分析使用二维而不是三维的可行性

#include <iostream>

using namespace std;
#define MAXN 110
int ans[MAXN][MAXN];

int main() {
	int n, m; //n个顶点,序号为 1 到 n, m条边
	while (cin >> n >> m) {
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				ans[i][j] = -1; //不可达
			}
			ans[i][i] = 0;
		}
		for (int i = 0; i < m; i++) { //顶点u到顶点v,代价为w
			int u, v, w;
			cin >> u >> v >> w; //顶点从1开始编号
			ans[u][v] = w;
			ans[v][u] = w;
		}
		for (int k = 1; k <= n; k++) {
	            for (int i = 1; i <= n; i++) {
		        for (int j = 1; j <= n; j++) {
			        if (ans[i][k] == -1 || ans[k][j] == -1)
				        continue;
			        if (ans[i][j] == -1 || ans[i][j] < ans[i][k] + ans[k][j])
				        ans[i][j] = ans[i][k] + ans[k][j];
		        }
	            }
                }
	 }

}

代码的核心就是三层for循环,三个for各有各的作用。首先要明确的是,三层for的最终目的就是要求出ans[n][i][j],ans[n][i][j]是将所有顶点能产生的影响都考虑进去时,各顶点对的最短路径。

①最内层for,k不变,i不变,j遍历所有顶点

                                                             

 

    比如k = 1,i = 3,j = 1...4,实现的是修改ans[3][j],顶点 3 到各顶点最小代价,即第三行值,考虑的是顶点k = 1的影响,顶点k = 1能对顶点 3 产生的影响就是,从顶点 3 走向顶点 1 ,再从顶点1去各顶点,这个代价与原来不考虑1的哪个小,就是

ans[3][j] = min(ans[3][1] + ans[1][j], ans[3][j]),从图上来看,3->1,应该找3行1列,再1->*,应该找1行*列,将这两步代价相加即3->*代价,图上是深蓝方块去加浅蓝方块,再与对应黄色方块比较,并修改黄色方块

最内层for修改的是,考虑需要小于k的顶点时,矩阵第i行的值,是顶点 i 考虑顶点k后的结果

②中间层for,j 遍历各个顶点,每个顶点都考虑一次顶点k产生的影响,就是顶点k对整个图产生的影响

③最外层for,k 遍历各个顶点,整个矩阵对每个顶点的影响都考虑一次,就是所有顶点对整个图产生的影响,即最终所求

二维替代三维

现在再来说明为什么能用二维数组替代三维数组

                                              

最外层for每轮要将这个二维数组所有元素修改一遍,从代码中可以看出,用来比较的,即需要其值不被随意改动的是一行一列的元素,修改其他9个白色方块时自然不会对这7个蓝色方块造成影响,而修改蓝色方块时呢?

①深蓝:即j = k,ans[i][k] = min(ans[i][k] + ans[k][k], ans[i][k]) ,ans[k][k]是自己到自己一定为0

②浅蓝:即i = k,ans[k][j] = min(ans[k][k] + ans[k][j], ans[k][j]),ans[k][k]同样为0

可以看出两种情况都不会造成影响,蓝色方块在这一轮k中是固定不变的,从算法上理解就是要考虑的中间顶点是k,如果k是两端结点,那么某个顶点经过k再到k,这个值就是这个顶点直接到k的值,另一端同理,从k经k到某个顶点=从k到某个顶点

既然没影响,自然可以在原地修改数组中的元素值


这里需要特别注意的是,最外层for每轮计算一个顶点能产生的影响,这个影响是该顶点在当前状态下能产生的直接影响,也就是顶点1能产生的影响,并不是在计算过k=1之后就固定了,只是在算法运行过程中,先把顶点1的影响加入,在后续过程中持续发挥着作用

比如,

k = 0时只将graph[i][j]考虑进去,

k = 1时将graph[i][j],graph[i][1] + graph[1][j]考虑进去

k = 2时将graph[i][j],graph[i][1] + graph[1][j],graph[i][1] + graph[1][2] + graph[2][j],graph[i][2] + graph[2][j]考虑进去,此时顶点1的影响不仅局限于k = 1那一轮计算过的graph[i][1] + graph[1][j],还有graph[i][1] + graph[1][2] + graph[2][j],先经1再经2

k = 2时看似是比较了两个值,其实是将四种路径进行比较取最小值,这样到最后一轮,就是所有路径的最小值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值