算法笔记:图论中的单源最短路径算法——Bellman-Ford 算法

Posted 无忧公主的数学时间

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法笔记:图论中的单源最短路径算法——Bellman-Ford 算法相关的知识,希望对你有一定的参考价值。


有一阵子没有写算法文章了吧,不知道大家有没有研究各种算法呢?还记得以前写过一篇 Dijkstra 算法的文章吧,在图论中可以求从一个节点出发,到每个节点的最短路径。虽然并不特别复杂,又比较有用,但还是有局限性的。今天介绍的 Bellman-Ford 算法,适用范围更广,我觉得代码可能还更简单一点哦。


下面是一些相关的链接,可以先阅读一下哦:






无忧公主



1
Dijkstra算法的局限


Dijkstra 算法是不能用于有负边的图,但实际上,有负权值边的图是可能存在最短路径的。一般的思想就是尽量走负边,但如果存在一个环,其权值之和为负,那就不存在最短路了。(前提保证图是连通的)原因其实比较好理解,从起点出发,一定能够走到这个负权环上。之后就无止境地沿着环绕啊绕,总权值越来越小,也就相当于没有最短路了。


2
Bellman-Ford算法


如果单是解决负权边的问题,有一种比较基本简单的思想,就是将边全部拓扑排序一下,然后按次序扫描一遍,不断将目前最短路总权值更新即可。不过,必须是有向无环图才能进行拓扑排序,那如果存在权值非负的环,该怎么做呢?


首先需要定义 dist[],即从起点出发,到每个点目前的最短路权值,这个在求单源最短路时是必备的。初始时起点设为 0,其余都设为 ∞(根据题目要求设定) 。如果需要记录下具体的路径,可以在做的过程当中,记录下每个点的前面一个节点。


我们可以扫描全部的边,但不需要给边排序。假设扫描到的边为 x -> y、权值为 w,此时更新 dist [ y ] 为 dist [ x ] + w,前提是 dist [ y ] 比 dist [ x ] + w 大,这个可以理解为取两者较小值。这样的扫描可以理解为,从起点一层一层地把权值刷过去。


显然扫描一遍是不够的,但最多只需要 n 遍,其中 n 为节点个数。原因就是,一条最短路至多经过全部的节点,这样刷 n 遍就一定能到达最远的地方了。这就是 Bellman-Ford 算法,这还有一个附带的 “ 功能 ”:如果刷到第 n + 1 遍,还有节点在更新,就说明存在负环。因此还可以利用这个算法来判断是否存在负环。


3
优化的策略(SPFA)


Bellman-Ford 算法是不够快的,需要两重循环,时间复杂度为 O ( n × m ),即节点数×边数。这是可以优化的,因为不一定需要扫描这么多遍,重复更新可能也是浪费的。说具体一些,即如果这轮 dist [ i ] 没有被更新,那么下一轮就没有必要更新所有从 i 出发的边。因为都是有向边,只有当 i 被更新了,到达的邻居才可能被更新。


那么我们可以将所有被更新的节点记录下来,存储容器用队列 queue 再合适不过了,即先进先出。每次从队头取出节点(这个节点保证之前被更新过),访问所有出边,如果这个邻居不在目前的 queue 中(不然不仅会慢,而且 queue 的大小不能控制,很可能超过内存限制)就再放到 queue 中。


之前的 Bellman-Ford 算法是可以判断是否存在负环的,但这种利用 queue 的做法已经没有 “ 第几遍刷 ” 这个循环记录了,那怎么判断呢?其实可以记录下 queue 中的每个节点是第几次被更新(放入 queue 中),如果超过 n,就说明有负环,可以直接退出。否则一直执行到 queue 为空,可以求出从起点出发到所有节点的最短路。


看上去真像 bfs 的思想啊,这个其实就是 SPFA 算法。Bellman-Ford 算法可能因为比较慢而并不怎么有名(但如果节点数只有 200、500 这个级别的,还是可以用的),但类似的 SPFA —— Shortest Path Faster Algorithm 估计有一些人会听说过吧。如果现在觉得并不难,那就去写代码吧!


4
C++ 示例代码


下面是 Bellman-Ford 算法的 C++ 示例代码(并不长哦),其中 ans 为图中是否存在负环,D[] 记录的就是从起点到每个节点的最短路,这里假设 1 号节点为起点。(可以根据题目要求进行调整和修改)



下面是 SPFA 算法的 C++ 示例代码,ans 和 D[] 的定义和 Bellman-Ford 算法的源代码中的定义是一样的,v[] 表示一个节点是否在 queue 中。(可以根据题目要求进行调整和修改)


5
后记


最后来讲点和算法无关、八卦好玩的吧。Bellman-Ford 算法是在 1955 年左右(时间上比Dijkstra算法还早几年),由 Bellman 和 Ford 同时分别发现的,所以称为 Bellman-Ford 算法。他们都是美国人,不过却都是数学家,因为当时的计算机并不普及,而在数学的学习中,是有图论这个部分的。Bellman-Ford 算法虽然有点慢,但是是求解最短路问题的基础框架,并且有很大优化余地的,到现在还有人在研究。之后,他们还发明了其他著名算法,大家有兴趣的可以自己上网查一下哦。


这种求单源最短路的图论算法确实有好几种:基于拓扑排序、Dijkstra 算法、Bellman-Ford 算法以及经过优化的 SPFA 算法。这些算法的适用范围各有不同,也有优缺点,大家可以先思考一下,我之后还可能写文章分析的哦,敬请期待!



这是我自己学习的笔记,大家如果有什么异议、问题或者建议,欢迎留言。



以上是关于算法笔记:图论中的单源最短路径算法——Bellman-Ford 算法的主要内容,如果未能解决你的问题,请参考以下文章

(单源最短路径)一文搞懂dijkstra算法

图论中四个最短路径算法

数据结构—— 图:最短路径问题

贪心算法—单源最短路径

Dijkstra

用小根堆实现dijkstra,求图的单源最短路径