单源最短路径Bellman-Ford算法:可处理负权边、负权回路情况

1、贝尔曼-福特(Bellman-Ford)相比迪杰斯特拉(Dijkstra)算法:

优缺点 Bellman-Ford Dijkstra
处理问题情况 可处理负权边、负权回路 只能处理正权边
时间复杂度 高,O(V*E) 低,O(V^2)
实现 代码实现简单 代码实现复杂

2、Bellman-Ford算法介绍:
Bellman-Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题。
2.1、算法步骤:

  • 初始化:将源点距离设为0(d[s]—>0),其余所有顶点的最短距离设为正无穷大( d[v] —>+∞)
  • 迭代求解:对边集E中每一条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离,循环V-1次(至此完成最短路径寻找)
  • 检验负权回路:执行第V次松弛,若距离仍然可以变小,说明存在负权环,算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在 d[v]中。

2.2、原理:
松弛
每次松弛操作实际上是对相邻节点的访问,第 n次松弛操作保证了所有深度为n的路径最短。 由于图的最短路径最长不会经过超过 |V|-1条边,所以循环V-1次。
负权环判定
因为负权环可以无限制的降低总花费(距离),所以如果发现第 n次操作仍可降低花销,就一定存在负权环。

2.3、优化:
循环的提前跳出
在实际操作中,贝尔曼-福特算法经常会在未达到 |V|-1次前就出解, |V|-1其实是最大值。于是可以在循环中设置判定,加一个flag,在某次循环不再进行松弛时,直接退出循环,进行负权环判定。

3、伪代码描述:
算法流程图解:算法流程图解
伪代码参考网址:Bellman-Ford算法原理

bool BellmanFord(list vertices, list edges, vertex source)
   // 该实现读入边和节点的列表,并向两个数组(distance和predecessor)中写入最短路径信息

   // 步骤1:初始化图
   for each vertex v in vertices:
       if v is source then distance[v] := 0 // 源节点距离为0
       else distance[v] := infinity //其余为正无穷
       predecessor[v] := null // 开始时没有路径

   // 步骤2:重复对每一条边进行松弛操作
   for i from 1 to size(vertices)-1: // V-1次循环
       for each edge (u, v) with weight w in edges: // 每一条边都松弛,边的顺序无关系
           if distance[u] + w < distance[v]:
               distance[v] := distance[u] + w
               predecessor[v] := u

   // 步骤3:检查负权环
   for each edge (u, v) with weight w in edges: 
       if distance[u] + w < distance[v]: // 仍然可以松弛则有负权环
           error "图包含了负权环"

参考资料:算法详细描述:百度百科——Bellman-Ford算法

总结:

1、无权图的最短路径算法为BFS;
2、有权图的最短路径当有负权边/环时用Bellman-Ford;
2、有权图的最短路径当没有负权边/环时用Dijkstra;

猜你喜欢

转载自blog.csdn.net/qq_33726635/article/details/106503058