【经典算法】Bellman-Ford最短路径算法 【原理理解】

更好的理解此算法的判负环:http://www.sohu.com/a/206351499_479559
[转自](https://blog.csdn.net/sms0101/article/details/73088422)

单源最短路径

给定一个图,和一个源顶点src,找到从src到其它所有所有顶点的最短路径,图中可能含有负权值的边。

Dijksra的算法是一个贪婪算法,时间复杂度是O(VLogV)(使用最小堆)。但是迪杰斯特拉算法在有负权值边的图中不适用,Bellman-Ford适合这样的图。在网络路由中,该算法会被用作距离向量路由算法。

Bellman-Ford也比迪杰斯特拉算法更简单和同时也适用于分布式系统。但Bellman-Ford的时间复杂度是O(VE),这要比迪杰斯特拉算法慢。(V为顶点的个数,E为边的个数)

算法描述

输入:图 和 源顶点src
输出:从src到所有顶点的最短距离。如果有负权回路(不是负权值的边),则不计算该最短距离,没有意义,因为可以穿越负权回路任意次,则最终为负无穷。

算法步骤

1.初始化:将除源点外的所有顶点的最短距离估计值 dist[v] ← +∞, dist[s] ←0;
2.迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)
3.检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在 dist[v]中。

关于该算法的证明也比较简单,采用反证法,具体参考:http://courses.csail.mit.edu/6.006/spring11/lectures/lec15.pdf
该算法是利用动态规划的思想。该算法以自底向上的方式计算最短路径。
它首先计算最多一条边时的最短路径(对于所有顶点)。然后,计算最多两条边时的最短路径。外层循环需要执行|V|-1次。

例子

一下面的有向图为例:给定源顶点是0,初始化源顶点距离所有的顶点都是是无穷大的,除了源顶点本身。因为有5个顶点,因此所有的边需要处理4次。

《【经典算法】Bellman-Ford最短路径算法 【原理理解】》

按照以下的顺序处理所有的边:(B,E), (D,B), (B,D), (A,B), (A,C), (D,C), (B,C), (E,D).
第一次迭代得到如下的结果(第一行为初始化情况,最后一行为最终结果):

当 (B,E), (D,B), (B,D) 和 (A,B) 处理完后,得到的是第二行的结果。
当 (A,C) 处理完后,得到的是第三行的结果。
当 (D,C), (B,C) 和 (E,D) 处理完后,得到第四行的结果。

《【经典算法】Bellman-Ford最短路径算法 【原理理解】》

第一次迭代保证给所有最短路径最多只有1条边。当所有的边被第二次处理后,得到如下的结果(最后一行为最终结果):

《【经典算法】Bellman-Ford最短路径算法 【原理理解】》

第二次迭代保证给所有最短路径最多只有2条边。我们还需要2次迭代(即所谓的松弛操作),就可以得到最终结果。

代码


 
 
  1. /*-------------------------------------
  2. * 题目: Bellman-Ford算法(单源最短路径)
  3. * 博客:
  4. ------------------------------------*/
  5. #include <iostream>
  6. using namespace std;
  7. //表示一条边
  8. struct Edge{
  9. // 起点
  10. int src;
  11. // 终点
  12. int dest;
  13. // 权重
  14. int weight;
  15. };
  16. //带权值的有向图
  17. struct Graph{
  18. // 顶点的数量
  19. int V;
  20. // 边的数量
  21. int E;
  22. // 用边的集合 表示一个图
  23. Edge* edge;
  24. };
  25. // 创建图
  26. Graph* CreateGraph( int v, int e){
  27. Graph* graph = (Graph*) malloc( sizeof(Graph));
  28. graph->E = e;
  29. graph->V = v;
  30. graph->edge = (Edge*) malloc(e* sizeof(Edge));
  31. return graph;
  32. }
  33. // 打印结果
  34. void Print( int dist[], int n){
  35. cout<< "单源最短路径:"<< endl;
  36. for( int i = 0;i < n;++i){
  37. if(dist[i] == INT_MAX){
  38. cout<< "与节点"<<i<< "距离->无穷大"<< endl;
  39. } //if
  40. else{
  41. cout<< "与节点"<<i<< "距离->"<<dist[i]<< endl;
  42. }
  43. } //for
  44. }
  45. // 单源最短路径
  46. bool BellmanFord(Graph* graph, int src){
  47. int v = graph->V;
  48. int e = graph->E;
  49. // 存储距离
  50. int dist[v];
  51. // 初始化
  52. for( int i = 0;i < v;++i){
  53. dist[i] = INT_MAX;
  54. } //for
  55. dist[src] = 0;
  56. // v-1次操作
  57. Edge edge;
  58. int a,b,weight;
  59. for( int i = 1;i < v;++i){
  60. // 对e条边进行松弛
  61. for( int j = 0;j < e;++j){
  62. edge = graph->edge[j];
  63. a = edge.src;
  64. b = edge.dest;
  65. weight = edge.weight;
  66. if(dist[a] != INT_MAX && dist[a]+weight < dist[b]){
  67. dist[b] = dist[a]+weight;
  68. } //if
  69. } //for
  70. } //for
  71. // 检测负权回路
  72. bool isBack = false;
  73. for( int i = 0;i < e;++i){
  74. edge = graph->edge[i];
  75. a = edge.src;
  76. b = edge.dest;
  77. weight = edge.weight;
  78. if(dist[a] != INT_MAX && dist[a]+weight < dist[b]){
  79. isBack = true;
  80. break;
  81. } //if
  82. } //for
  83. // 打印结果
  84. Print(dist,v);
  85. return isBack;
  86. }
  87. int main(){
  88. int v = 7;
  89. int e = 9;
  90. Graph* graph = CreateGraph(v,e);
  91. graph->edge[ 0].src = 0;
  92. graph->edge[ 0].dest = 1;
  93. graph->edge[ 0].weight = - 1;
  94. graph->edge[ 1].src = 0;
  95. graph->edge[ 1].dest = 2;
  96. graph->edge[ 1].weight = 4;
  97. graph->edge[ 2].src = 1;
  98. graph->edge[ 2].dest = 2;
  99. graph->edge[ 2].weight = 3;
  100. graph->edge[ 3].src = 1;
  101. graph->edge[ 3].dest = 3;
  102. graph->edge[ 3].weight = 2;
  103. graph->edge[ 4].src = 1;
  104. graph->edge[ 4].dest = 4;
  105. graph->edge[ 4].weight = 2;
  106. graph->edge[ 5].src = 3;
  107. graph->edge[ 5].dest = 2;
  108. graph->edge[ 5].weight = 5;
  109. graph->edge[ 6].src = 3;
  110. graph->edge[ 6].dest = 1;
  111. graph->edge[ 6].weight = 1;
  112. graph->edge[ 7].src = 4;
  113. graph->edge[ 7].dest = 3;
  114. graph->edge[ 7].weight = - 3;
  115. graph->edge[ 8].src = 5;
  116. graph->edge[ 8].dest = 6;
  117. graph->edge[ 8].weight = 2;
  118. bool result = BellmanFord(graph, 0);
  119. if(result){
  120. cout<< "图中存在回路"<< endl;
  121. } //if
  122. else{
  123. cout<< "图中不存在回路"<< endl;
  124. } //else
  125. return 0;
  126. }

,

    原文作者:Bellman - ford算法
    原文地址: https://blog.csdn.net/SunPeishuai/article/details/84799238
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞