Bellman-Ford 算法是一种用于计算带权有向图中单源最短路径(SSSP:Single-Source Shortest Path)的算法。该算法由 Richard Bellman 和 Lester Ford 分别发表于 1958 年和 1956 年,而实际上 Edward F. Moore 也在 1957 年发布了相同的算法,因此,此算法也常被称为 Bellman-Ford-Moore 算法。

Bellman-Ford 算法和 Dijkstra 算法同为解决单源最短路径的算法。对于带权有向图 G = (V, E),Dijkstra 算法要求图 G 中边的权值均为非负,而 Bellman-Ford 算法能适应一般的情况(即存在负权边的情况)。一个实现的很好的 Dijkstra 算法比 Bellman-Ford 算法的运行时间要低。

Bellman-Ford 算法采用动态规划(Dynamic Programming)进行设计,实现的时间复杂度为 O(V*E),其中 V 为顶点数量,E 为边的数量。Dijkstra 算法采用贪心算法(Greedy Algorithm)范式进行设计,普通实现的时间复杂度为 O(V2),若基于 Fibonacci heap 的最小优先队列实现版本则时间复杂度为 O(E + VlogV)。

Bellman-Ford 算法描述:

  1. 创建源顶点 v 到图中所有顶点的距离的集合 distSet,为图中的所有顶点指定一个距离值,初始均为 Infinite,源顶点距离为 0;
  2. 计算最短路径,执行 V - 1 次遍历;
    • 对于图中的每条边:如果起点 u 的距离 d 加上边的权值 w 小于终点 v 的距离 d,则更新终点 v 的距离值 d;
  3. 检测图中是否有负权边形成了环,遍历图中的所有边,计算 u 至 v 的距离,如果对于 v 存在更小的距离,则说明存在环;

伪码实现如下:

 BELLMAN-FORD(G, w, s)
INITIALIZE-SINGLE-SOURCE(G, s)
for i to |V[G]| -
do for each edge (u, v) E[G]
do RELAX(u, v, w)
for each edge (u, v) E[G]
do if d[v] > d[u] + w(u, v)
then return FALSE
return TRUE

Bellman-Ford 算法的运行时间为 O(V*E),因为第 2 行的初始化占用了 Θ(V),第 3-4 行对边进行了 V - 1 趟操作,每趟操作的运行时间为 Θ(E)。第 6-7 行的 for 循环运行时间为 O(E)。

例如,下面的有向图 G 中包含 5 个顶点和 8 条边。假设源点 为 A。初始化 distSet 所有距离为 INFI,源点 A 为 0。

由于图中有 5 个顶点,按照步骤 1 需要遍历 4 次,第一次遍历的结果如下。

第二次遍历的结果如下。

以此类推可以得出完全遍历的结果。

C# 代码实现:

 using System;
using System.Collections.Generic;
using System.Linq; namespace GraphAlgorithmTesting
{
class Program
{
static void Main(string[] args)
{
int[,] graph = new int[, ]
{
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , }
}; Graph g = new Graph(graph.GetLength());
for (int i = ; i < graph.GetLength(); i++)
{
for (int j = ; j < graph.GetLength(); j++)
{
if (graph[i, j] > )
g.AddEdge(i, j, graph[i, j]);
}
} Console.WriteLine("Graph Vertex Count : {0}", g.VertexCount);
Console.WriteLine("Graph Edge Count : {0}", g.EdgeCount);
Console.WriteLine(); int[] distSet = g.BellmanFord();
Console.WriteLine("Vertex\t\tDistance from Source");
for (int i = ; i < distSet.Length; i++)
{
Console.WriteLine("{0}\t\t{1}", i, distSet[i]);
} // build a directed and negative weighted graph
Graph directedGraph = new Graph();
directedGraph.AddEdge(, , -);
directedGraph.AddEdge(, , );
directedGraph.AddEdge(, , );
directedGraph.AddEdge(, , );
directedGraph.AddEdge(, , );
directedGraph.AddEdge(, , );
directedGraph.AddEdge(, , );
directedGraph.AddEdge(, , -); Console.WriteLine();
Console.WriteLine("Graph Vertex Count : {0}", directedGraph.VertexCount);
Console.WriteLine("Graph Edge Count : {0}", directedGraph.EdgeCount);
Console.WriteLine(); int[] distSet1 = directedGraph.BellmanFord();
Console.WriteLine("Vertex\t\tDistance from Source");
for (int i = ; i < distSet1.Length; i++)
{
Console.WriteLine("{0}\t\t{1}", i, distSet1[i]);
} Console.ReadKey();
} class Edge
{
public Edge(int begin, int end, int weight)
{
this.Begin = begin;
this.End = end;
this.Weight = weight;
} public int Begin { get; private set; }
public int End { get; private set; }
public int Weight { get; private set; } public override string ToString()
{
return string.Format(
"Begin[{0}], End[{1}], Weight[{2}]",
Begin, End, Weight);
}
} class Graph
{
private Dictionary<int, List<Edge>> _adjacentEdges
= new Dictionary<int, List<Edge>>(); public Graph(int vertexCount)
{
this.VertexCount = vertexCount;
} public int VertexCount { get; private set; } public int EdgeCount
{
get
{
return _adjacentEdges.Values.SelectMany(e => e).Count();
}
} public void AddEdge(int begin, int end, int weight)
{
if (!_adjacentEdges.ContainsKey(begin))
{
var edges = new List<Edge>();
_adjacentEdges.Add(begin, edges);
} _adjacentEdges[begin].Add(new Edge(begin, end, weight));
} public int[] BellmanFord(int source)
{
// distSet[i] will hold the shortest distance from source to i
int[] distSet = new int[VertexCount]; // Step 1: Initialize distances from source to all other vertices as INFINITE
for (int i = ; i < VertexCount; i++)
{
distSet[i] = int.MaxValue;
}
distSet[source] = ; // Step 2: Relax all edges |V| - 1 times. A simple shortest path from source
// to any other vertex can have at-most |V| - 1 edges
for (int i = ; i <= VertexCount - ; i++)
{
foreach (var edge in _adjacentEdges.Values.SelectMany(e => e))
{
int u = edge.Begin;
int v = edge.End;
int weight = edge.Weight; if (distSet[u] != int.MaxValue
&& distSet[u] + weight < distSet[v])
{
distSet[v] = distSet[u] + weight;
}
}
} // Step 3: check for negative-weight cycles. The above step guarantees
// shortest distances if graph doesn't contain negative weight cycle.
// If we get a shorter path, then there is a cycle.
foreach (var edge in _adjacentEdges.Values.SelectMany(e => e))
{
int u = edge.Begin;
int v = edge.End;
int weight = edge.Weight; if (distSet[u] != int.MaxValue
&& distSet[u] + weight < distSet[v])
{
Console.WriteLine("Graph contains negative weight cycle.");
}
} return distSet;
}
}
}
}

运行结果如下:

参考资料

本篇文章《Bellman-Ford 单源最短路径算法》由 Dennis Gao 发表自博客园,未经作者本人同意禁止任何形式的转载,任何自动或人为的爬虫转载行为均为耍流氓。

Bellman-Ford 单源最短路径算法的更多相关文章

  1. 【模板】Bellman—Fort 单源最短路径算法

    2333 适用于边集储存 #include<bits/stdc++.h> using namespace std; const int inf=0x3fffffff; ],t[],d[], ...

  2. Dijkstra 单源最短路径算法

    Dijkstra 算法是一种用于计算带权有向图中单源最短路径(SSSP:Single-Source Shortest Path)的算法,由计算机科学家 Edsger Dijkstra 于 1956 年 ...

  3. 经典贪心算法(哈夫曼算法,Dijstra单源最短路径算法,最小费用最大流)

    哈夫曼编码与哈夫曼算法 哈弗曼编码的目的是,如何用更短的bit来编码数据. 通过变长编码压缩编码长度.我们知道普通的编码都是定长的,比如常用的ASCII编码,每个字符都是8个bit.但在很多情况下,数 ...

  4. 单源最短路径算法:迪杰斯特拉 (Dijkstra) 算法(二)

    一.基于邻接表的Dijkstra算法 如前一篇文章所述,在 Dijkstra 的算法中,维护了两组,一组包含已经包含在最短路径树中的顶点列表,另一组包含尚未包含的顶点.使用邻接表表示,可以使用 BFS ...

  5. 单源最短路径算法:迪杰斯特拉 (Dijkstra) 算法(一)

    一.算法介绍 迪杰斯特拉算法(英语:Dijkstra's algorithm)由荷兰计算机科学家艾兹赫尔·迪杰斯特拉在1956年提出.迪杰斯特拉算法使用了广度优先搜索解决赋权有向图的单源最短路径问题. ...

  6. 单源最短路径算法---Dijkstra

    Dijkstra算法树解决有向图G=(V,E)上带权的单源最短路径问题,但是要求所有边的权值非负. 解题思路: V表示有向图的所有顶点集合,S表示那么一些顶点结合,从源点s到该集合中的顶点的最终最短路 ...

  7. Dijkstra算法详细(单源最短路径算法)

    介绍 对于dijkstra算法,很多人可能感觉熟悉而又陌生,可能大部分人比较了解bfs和dfs,而对dijkstra和floyd算法可能知道大概是图论中的某个算法,但是可能不清楚其中的作用和原理,又或 ...

  8. 单源最短路径算法——Dijkstra算法(迪杰斯特拉算法)

    一 综述 Dijkstra算法(迪杰斯特拉算法)主要是用于求解有向图中单源最短路径问题.其本质是基于贪心策略的(具体见下文).其基本原理如下: (1)初始化:集合vertex_set初始为{sourc ...

  9. 单源最短路径算法——Bellman-ford算法和Dijkstra算法

     BellMan-ford算法描述 1.初始化:将除源点外的所有顶点的最短距离估计值 dist[v] ← +∞, dist[s] ←0; 2.迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V ...

随机推荐

  1. ASP.NET Core 1.1 简介

    ASP.NET Core 1.1 于2016年11月16日发布.这个版本包括许多伟大的新功能以及许多错误修复和一般的增强.这个版本包含了多个新的中间件组件.针对Windows的WebListener服 ...

  2. Visual Studio Code 代理设置

    Visual Studio Code (简称 VS Code)是由微软研发的一款免费.开源的跨平台文本(代码)编辑器,在十多年的编程经历中,我使用过非常多的的代码编辑器(包括 IDE),例如 Fron ...

  3. 百度 flash html5自切换 多文件异步上传控件webuploader基本用法

    双核浏览器下在chrome内核中使用uploadify总有302问题,也不知道如何修复,之所以喜欢360浏览器是因为帮客户控制渲染内核: 若页面需默认用极速核,增加标签:<meta name=& ...

  4. HTML文档声明

    前面的话   HTML文档通常以类型声明开始,该声明将帮助浏览器确定其尝试解析和显示的HTML文档类型.本文将详细介绍文档声明DOCTYPE 特点   文档声明必须是HTML文档的第一行.且顶格显示, ...

  5. 比Mysqli操作数据库更简便的方式 。PDO

    下面来说一下PDO 先画一张图来了解一下 mysqli是针对mysql这个数据库扩展的一个类 PDO是为了能访问更多数据库 如果出现程序需要访问其他数据库的话就可以用PDO来做 PDO数据访问抽象层1 ...

  6. angular2系列教程(九)Jsonp、URLSearchParams、中断选择数据流

    大家好,今天我们要讲的是http模块的第二部分,主要学习ng2中Jsonp.URLSearchParams.observable中断选择数据流的用法. 例子

  7. 【Java大系】Java快速教程

    感谢原作者:Vamei 出处:http://www.cnblogs.com/vamei Java是面向对象语言.这门语言其实相当年轻,于1995年才出现,由Sun公司出品.James Gosling领 ...

  8. MementoPattern(备忘录模式)

    /** * 备忘录模式 * @author TMAC-J * 用于存储bean的状态 */ public class MementoPattern { public class Memento{ pr ...

  9. Lind.DDD.LindMQ的一些想法

    回到目录 很久就想写一套属于自己的消息队列组件,前段时候看了汤雪华同学的EQueue,感觉还是不错的,他也是看了rabbitMQ之后写的Equeue,在设计上与前者有类似的地方,而大叔这次准备写一个L ...

  10. Struts2入门(六)——国际化

    一.前言 1.1.国际化简介 国际化是指应用程序在运行的时候,根据客户端请求来自的国家地区.语言的不同而显示不同的界面(简单说就是根据你的地区显示相关地区的语言,如果你现在在英国,那么显示的语言就是英 ...