5种经典最短路径算法联系与区别
5种经典路径算法指的是(出场顺序根据文章思路进行),本文不再赘述其各自的概念与内容(不清楚的请先查阅其他大佬的博客),按本人理解从中发觉其中的联系与区别。
首先最经典的 算法,我们通过其算法过程,可以概括为贪婪思想,逐步扩展,为什么这么说,根据算法过程,例如A点为起点,那么就有一个列表即A到各点的距离,从整体看每次都在更新列表以达到最小目的。 那是如何更新呢,分两步:
一是从每次列表中取最小的点(记为M)即为A到此点M的最短距离,取最小即贪婪思想。
二是以此找出最小的点为桥接点,通过比较A -> 终点 还是A->M->终点 大小进行“松弛“,(常见的松弛就是比较个大小而已),松弛完毕每个点,则更新列表值一次。 下次更新从更新后的剩余点中找最小。直到遍历所有点结束
因为每次更新都是以第一步中的最小值为基础,所以这也是为什么此算法只能用于正值的根本所在,简单来说就是算法特点不会重回此点再去松弛,所以有负值时出现非最佳结果。
那么我们可以看到Dijkstra 只能正权是因为不能回点松弛,那么为什么我非要取最小值为桥接点呢,我找一种办法让他可以回点松弛不就可以是负权了嘛,所以后来又产生了另一种算法叫贝尔曼福德。
利用了最短路径的边特点,什么特点呢,即假设无回路下有N个点,查找最短路径最多松弛N-1次就有结果了,即把每个点都通过一遍。那贝尔曼福德咋做的呢,直接循环N-1次,每次都对各边松弛一下,这样绝对不会漏掉,简单粗暴,循环完N-1,我再来一遍松弛,发现有边还在变化,这是怎么了,这不符合我们的背景啊,所以出现这种情况即出现了回路,所以贝尔曼也是通过这个方法判断是否有回路存在。
有人应该又看出了,我每次都松弛每个边,明明好多边都不用变的啊,这不浪费时间嘛,有没有一种办法能够精准定位松弛情况呢,所以又出现了一种算法解决这个问题,就是SPFA。
算法一般博客中都说这个叫做队列优化后的贝尔曼,为啥这么说? 其实很简单,所谓的队列优化就是用了广度优先算法BFS思想,BFS一般是用队列来实现的,好了有了这个能干嘛,假如我们还是那个A点到各点距离的列表,我们不找最小了,我们从起点开始找他的连通节点,连通节点松弛了,说明什么,说明连通节点松弛了,那么连通节点的连通节点有可能需要松弛啊,这里有点绕,简单来说就是我是小B和小C的哥,小D的叔,我娶了小B的姐,小B得换姐夫,小C还是哥,小D没他啥事 肯定还是叔,一个道理,根据这个特点,我们可以广度遍历的时候,每次都去判断是否去松弛 松弛过的点 所连通的点,这个操作就是通过队列来实现。
好了,现在已经说了三个算法了,我们从宏观会发现,找到最短路径怎么也得遍历到所有的点或者说边才能找到,那么有没有一种直接关心遍历问题而产生的算法呢,Floyd即为此。
又称为插点法,为啥,他就是关心你到最短路径是否考虑到了存在的所有点,从动态规划的角度,可以从小到大,比如有N个点,一开始我就只考虑起点与终点之间就一个点的时候,很好算松弛一下是吧,那么往下考虑有俩点呢,在之前基础上再松弛一下,好了考虑到所有点之后,最短路径就出来了。思想简单,代码更简单。自行查找。
最后一个A算法,与前边都不同,他属于启发式,但是并不无联系,他最大联系在于与Dijlstra的联系,Dijlstra应用的是实际距离作为判断数据,他无方向性的去判断最小,贪婪前行,性能肯定比有方向的更差,那有什么办法让算法向结果方向进行呢,即A
之所以叫启发式,是因为他有一个很特别的东西叫评估函数,f=g+h, g可以看作Dijlstra中的实际距离,h为评估距离,而A比较的依据是f值,Dijlstra依据是g值,所以很容易看到联系,当h为0时,两者相等,A退化为Dijlstra,h=0,说明没有方向性, 那h是啥,咋确定,一般应用曼哈顿距离,就是X,Y各轴相差的绝对值和。 A具体算法不再赘述,靠理解来说就是Dijlstra判断最小用g值,松弛更新也用g值判断, 而A判断最小用f值,松弛更新用g值判断。 本质区别就是f含有向终点方向的程度值,使得朝向终点找最短路径,无疑更为快速。
以上,认真理解的小伙伴,应该已经看到了他们的区别与联系。简单总结:
- Dijkstra 适用范围:单源 正权 无回路 方式:贪婪无向扩展
- 贝尔曼 适用范围:单源 正负权 无回路 方式:暴力松弛
- SPFA 适用范围:单源 正负权 无回路 方式:更精确去松弛的贝尔曼
- Floyd 适用范围:多源 正负权 无回路 方式:遍历考虑所有点
- A* 适用范围:点对 正权 无回路 方式:贪婪评估有向扩展
所有以上算法 都是静态图,即点不会动的去查找, 目前更高级的人工智能机器人都需要动态查找路径,D* ,D* Lite之类的算法产生,有兴趣的同学继续深入~。