同为最短路径算法,单源节点至其他所有节点的最短路径,Bellman-Ford算法由美国数学家、动态规划创始人Richard Bellman及Lester Ford发明于20世纪50年代。该算法的提出主要是基于这样一个事实:如果一个图中没有权值和为负值的回路(non-negative-weight cycle),那么从某个节点至其他节点的最短路径不会超过|V|-1条边,其中V表示一个图的定点个数。所以在这个算法了里,我们循环地做|V|-1次,每次对所有的边进行松弛(relax),循环结束之后距离数组中的值就是最后的结果。
/*
* szl_bellman_ford.h
*/
#ifndef SZL_BELLMAN_FORD_H
#define SZL_BELLMAN_FORD_H
void szl_bellman_ford(int* graph[], int source, int destination);
#endif
/*
* szl_bellman_ford.c
*/
#include "szl_bellman_ford.h"
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
void szl_bellman_ford(int* graph[], int source, int destination);
int n,m;
int main( int argc ,char ** argv){
int **graph;
int i,j;
int u,v,w;
/*
* the source node and the destination node
*/
int s,d;
/*
* read from a file in the current directory named 'data.txt'
*/
FILE* data_file;
if( 1 == argc){
data_file=fopen("data.txt", "r");
}
else{
data_file=fopen(argv[1], "r");
}
assert(NULL!=data_file);
//printf("input vertices and edges.\n");
fscanf(data_file,"%d%d",&n,&m);
/*
* implement a graph with matrix
*/
graph=(int **)malloc(sizeof(int *) * n);
assert(NULL != graph);
for(i=0;i<n;i++){
graph[i]=(int *)malloc(sizeof(int)*n);
assert(NULL!=graph[i]);
}
/*
* initialization the weight of the graph with a value which means this edge is not exists.
*/
for(i=0;i<n;i++){
for(j=0;j<n;j++){
graph[i][j]=INT_MIN;
}
}
/*
* input the edge and its weight
*/
for(i=0;i<m;i++){
//printf("input edge %d:\n", (1+i));
fscanf(data_file,"%d%d%d",&u,&v,&w);
graph[u][v]=w;
}
/*
* get the starting node and the ending node
*/
//printf("input destination\n");
fscanf(data_file,"%d%d",&s,&d);
/*
* call the dijkstra to address it
*/
szl_bellman_ford(graph,s, d);
/*
* close the input stream and free the memory allocated manually.
*/
fclose(data_file);
for(i=0;i<n;i++){
free(graph[i]);
}
free(graph);
return 0;
}
void szl_bellman_ford(int* graph[], int source, int destination){
/*
* store the predecessor of each node in the shortest path for print later.
*/
int *predecessor = (int *)malloc(sizeof(int)*n);
/*
* store the distance from the starting node to all the nodes in the graph
*/
int *dist=(int *)malloc(sizeof(int)*n);
int *dist_last =( int *)malloc(sizeof(int)*n);
/*
* indicate whether the distance value of a node is infinite or finite.
*/
int *dis_infinite = (int *)malloc(sizeof(int)*n);
int *dis_infinite_last = (int *)malloc(sizeof(int )*n);
/*
* store the path, which can be evaluated from the predecessor array.
*/
int *stack=(int *)malloc(sizeof(int)*n);
int i,j,k;
int u,v,w;
int s=source;
//initialization
for(i=0;i<n;i++){
predecessor[i] = -1;
dis_infinite[i] = 1;
dis_infinite_last[i] = 1;
dist[i] = 0;
dist_last[i] = 0;
}
/*
* the distance from source node to itself is zero, so it is not inifite large.
*/
dis_infinite[s] = 0;
dis_infinite_last[s] = 0;
dist[s] = 0;
dist_last[s] = 0;
//relax
for(i=1;i<n;i++){
/*
* copy data
*/
for(j=0;j<n;j++){
dist_last[j] = dist[j];
dis_infinite_last[j] = dis_infinite[j];
}
/*
* address all adjecent edges of the newly selected node.
*/
for(u=0;u<n;u++){
for(v=0;v<n;v++){
if(!(graph[u][v] == INT_MIN)){ /* if (u,v) is an edge */
if(dis_infinite_last[u] == 0){ /* if this node has a finite large distance. */
if(dis_infinite_last[v] != 0){
dist[v] = dist_last[u] + graph[u][v];
predecessor[v] = u;
dis_infinite[v] = 0;
}
else if(dist[v] > dist_last[u] + graph[u][v]){
dist[v] = dist_last[u] + graph[u][v];
predecessor[v] = u;
}
}
}
}
}
}
/**
* check negative-weight cycle
*/
for(u=0;u<n;u++){
for(v=0;v<n;v++){
if(graph[u][v] != INT_MIN){
if(dis_infinite[u] == 0){
if((dist[u] + graph[u][v] < dist[v]) || dis_infinite[v] != 0){
fscanf(stdout, "There exists negative-weight cycle.\n");
}
}
}
}
}
//print
j=destination;
i=0;
while(predecessor[j] != -1){
stack[i++] = j;
j=predecessor[j];
}
stack[i] = j;
printf("path length = %d\n", dist[destination]);
for(j=i;j>=0;j--){
printf("%d",stack[j]);
if(j>0){
printf(", ");
}
}
//free
free(dist);
free(dist_last);
free(dis_infinite);
free(predecessor);
free(stack);
}
下面是测试数据,首先是没有负权的有向图(这个图和上一篇博客dijkstra算法的测试图示一样的):
文件data2.txt:
8 15
0 2 9
0 6 14
0 7 15
2 3 24
3 5 2
3 1 19
4 3 6
4 1 6
5 4 11
5 1 16
6 3 18
6 5 30
6 7 5
7 5 20
7 1 44
0 1
即图:
执行:
看见结果是正确的。
测试文件data.txt
9 16
0 1 9
0 2 7
0 3 4
1 4 -4
2 3 -4
2 5 3
3 1 5
3 4 3
3 6 2
4 6 -2
4 8 -3
5 3 -8
5 7 -3
6 7 5
6 8 2
7 8 -7
0 8
测试结果:
也就是这图(从网上下载的):