Floyd是咋求图的最短路径?(一)
前言
在图论中,在寻路最短路径中除了Dijkstra算法以外,还有Floyd算法也是非常经典,然而两种算法还是有区别的,Floyd主要计算多源最短路径。
在单源正权值最短路径,我们会用Dijkstra算法来求最短路径,并且算法的思想很简单—贪心算法:每次确定最短路径的一个点然后维护(更新)这个点周围点的距离加入预选队列,等待下一次的抛出确定。虽然思想很简单,实现起来是非常复杂的,我们需要邻接矩阵(表)储存长度,需要优先队列(或者每次都比较)维护一个预选点的集合。还要用一个boolean数组标记是否已经确定、还要……
总之,Dijkstra算法的思想上是很容易接受的,但是实现上其实是非常麻烦的。但是单源最短路径解算暂时还没有有效的办法,复杂度也为O(n2)。
而在n点图中想求多源最短路径,如果从Dijkstra算法的角度上,需要将Dijkstra执行n次才能获得所有点之间的最短路径,不过执行n次Dijkstra算法即可,复杂度为O(n3)。但是这样感觉很臃肿,代码量巨大,占用很多空间内存。有没有啥方法能够稍微变变口味呢?
答案是有的,今天就带大家一起了解一下牛逼轰轰的Floyed算法。
算法介绍
什么是Floyed算法?
Floyd算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。
简单的来说,算法的主要思想是动态规划(dp),而求最短路径需要不断松弛(熟悉spfa算法的可能熟悉松弛)。
而算法的具体思想为:
1 .邻接矩阵(二维数组)dist储存路径,数组中的值开始表示点点之间初始直接路径,最终是点点之间的最小路径,有两点需要注意的,第一是如果没有直接相连的两点那么默认为一个很大的值(不要因为计算溢出成负数),第二是自己和自己的距离要为0。
2 .从第1个到第n个点依次加入松弛计算,每个点加入进行试探枚举是否有路径长度被更改(自己能否更新路径)。顺序加入(k枚举)松弛的点时候,需要遍历图中每一个点对(i,j双重循环),判断每一个点对距离是否因为加入的点而发生最小距离变化,如果发生改变(变小),那么两点(i,j)距离就更改。
2 .重复上述直到最后插点试探完成。
其中第2步的状态转移方程为:
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j])
其中dp[a][b]的意思可以理解为点a到点b的最短路径,所以dp[i][k]的意思可以理解为i到k的最短路径dp[k][j]的意思为k到j的最短路径.
咱们图解一个案例,初始情况每个点只知道和自己直接相连的点的距离,而其他间接相连的点还不知道距离,比如A-B=2,A-C=3但是B-C在不经过计算的情况是不知道长度的。
加入第一个节点A进行更新计算,大家可以发现,由于A的加入,使得本来不连通的B,C点对和B,D点对变得联通,并且加入A后距离为当前最小,同时你可以发现加入A其中也使得C-D多一条联通路径(6+3),但是C-D联通的话距离为9远远大于本来的(C,D)联通路径2,所以这条不进行更新。
咱们继续加入第二个节点B,这个点执行和前面A相同操作进行。对一些点进行更新。因为和B相连的点比较多,可以产生很多新的路径,这里给大家列举一下并做一个说明,这里新路径我统一用1表示,原来长度用0表示。
AF1=AB+BF=6+2=8 < AF0(∞) 进行更新
AE1=AB+BE=2+4=6 < AE0(∞) 进行更新
CE1=CB+BE=5+5=9 < CE0(∞) 进行更新
CF1=CB+BF=5+6=11<CF0(∞) 进行更新
EF1=EB+BF=4+6=10<EF0(∞) 进行更新
当然,也有一些新的路径大于已有路径不进行更新,例如:
AC1=AB+BC=2+5=7>AC0(3) 不更新
AD1=AB+BD=2+8=10>AD0(6) 不更新
……
更多路径这里就不一一列举了。
后序加入C、D、E、F都是进行相同的操作,最终全部加完没有路径可以更新就结束停止。实际上这个时候图中的连线就比较多了。这些连线都是代表当前的最短路径。 这也和我们的需求贴合,我们最终要的是所有节点的最短路径。每个节点最终都应该有5条指向不同节点的边! 矩阵对应边值就是点点之间最短路径。
至于算法的模拟两部核心已经告诉大家了,大家可以自行模拟剩下的。
文章转自公众号:bigsai