bellman-ford算法

本文深入解析Bellman-Ford算法,一种用于寻找单源节点到其他所有节点最短路径的高效算法。通过具体实例和代码实现,详细解释算法原理和应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

同为最短路径算法,单源节点至其他所有节点的最短路径,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

测试结果:


也就是这图(从网上下载的):


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值