一、算法概述
- Bellman-Ford算法,也可以翻译成中文“贝尔曼-福特算法”,常常拿来和Dijkstra算法一起比较理解。
- Bellman-Ford算法的原理是对图进行 V-1 次松弛操作,算法的复杂度高达O(VE)。但是相对于Dijkstra算法,尽管算法复杂度稍高一些,但是它能处理带负权值边而没有负权回路的图。V-1次松弛操作,必定能求出最短路径,因为图的深度最多为V-1.
二、实现步骤
- 数组 distance[v] 表示从源点source到顶点v的最短路径,即为要求的答案。
- 数组 predecessor[v] 表示从源点source到顶点v的最短路径p(s,….,v),顶点v的前一个节点。可以由此数组导出一个前驱子图或以source为根的最短路径树。
- w表示边 (u,v) 的权值。
- 初始化操作:一开始从source到自己的距离为0,到其他所有点的最短距离为正无穷大。
- 进行 V-1 次松弛操作:每一次对所有的边遍历一遍,若distance[u] + w < distance[v],则更新distance[v]和predecessor[v]。
- 检查是否存在负权回路:对每条边遍历一遍,若仍然可以更新distance,说明存在负权回路。
三、伪代码实现
摘自维基百科的贝尔曼-福特算法中文词条。其中的过程可以改写成带布尔型变量返回值的函数。若存在负权回路,则返回false,否则返回true。distance和predecessor可以设置为全局变量。
procedure BellmanFord(list vertices, list edges, vertex source)
// 该实现读入边和节点的列表,并向两个数组(distance和predecessor)中写入最短路径信息
// 步骤1:初始化图
for each vertex v in vertices:
if v is source then distance[v] := 0
else distance[v] := infinity
predecessor[v] := null
// 步骤2:重复对每一条边进行松弛操作
for i from 1 to size(vertices)-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 "图包含了负权环"
四、C++实现
注意这里是有向图的实现。顶点是从1开始的,所以下面的代码中有三处的for循环是从1开始而非0;
/*
* Bellman-Ford.cpp
* Author: Halfish Zhang
* Creat_Time: 5/19/2014
*/
#include <iostream>
#include <stack>
using namespace std;
const int INF_NUM = 0x3f3f3f3f; // momerize this const
const int N = 2014;
struct Edge
{
int u, v; // edge (u,v)
int weight; // the cost(or weight) of edge (u,v)
};
int vertexNum, edgeNum, source;
int distances[N], predecessor[N];
Edge edges[N];
bool Bellman_Ford()
{
cout << "Bellman_Ford called" << endl;
// Step One: initialize
for(int i = 1; i <= edgeNum; ++ i)
{
distances[i] = INF_NUM; // you can also use "memset(distances, 0x3f, edgeNum)" to initialize;
predecessor[i] = i;
}
distances[source] = 0;
// Step Two: relaxation technique, complexity is O(VE)
for(int i = 1; i <= vertexNum - 1; ++ i)
{
for(int j = 0; j < edgeNum; ++ j)
{
if(distances[edges[j].v] > distances[edges[j].u] + edges[j].weight)
{
// update distances and predecessor
distances[edges[j].v] = distances[edges[j].u] + edges[j].weight;
predecessor[edges[j].v] = edges[j].u;
}
}
}
// Step Three: check if the cycle with nagative weight exists
bool flag = true;
for(int i = 0; i < edgeNum; ++ i)
{
if(distances[edges[i].v] > distances[edges[i].u] + edges[i].weight)
{
flag = false;
break;
}
}
return flag;
}
void print_shortest_path(int destination)
{
cout << "print_shortest_path called" << endl;
stack<int> s;
s.push(destination);
while(predecessor[s.top()] != source)
{
s.push(predecessor[s.top()]);
}
s.push(source);
cout << "The Path of ( " << source << ", " << destination << " ) is: " << endl;
int len = s.size();
for(int i = 0; i < len; ++ i)
{
cout << s.top() << " ";
s.pop();
}
cout << endl;
}
int main(int argc, char const *argv[])
{
cin >> vertexNum >> edgeNum >> source;
for(int i = 0; i < edgeNum; ++ i) {
cin >> edges[i].u >> edges[i].v >> edges[i].weight;
}
if ( Bellman_Ford() ) {
//test_print();
for(int i = 0; i < vertexNum; ++ i)
{
//attention: edges are numbered 1...N
print_shortest_path(i+1);
}
} else {
cout << "Sorry, a cycle with negative weight exists!";
}
return 0;
}
五、样例
见下图:
输入样例
6 9 1
1 2 7
1 3 9
1 6 14
2 3 10
2 4 15
3 4 11
3 6 2
4 5 6
5 6 9
输出样例
Bellman_Ford called
print_shortest_path called
The Path of ( 1, 1 ) is:
1 1
print_shortest_path called
The Path of ( 1, 2 ) is:
1 2
print_shortest_path called
The Path of ( 1, 3 ) is:
1 3
print_shortest_path called
The Path of ( 1, 4 ) is:
1 3 4
print_shortest_path called
The Path of ( 1, 5 ) is:
1 3 4 5
print_shortest_path called
The Path of ( 1, 6 ) is:
1 3 6
--------------------------------
Process exited with return value 0
Press any key to continue . . .