1. 程式人生 > >Bellman-Ford 最短路徑演算法 圖示與實現

Bellman-Ford 最短路徑演算法 圖示與實現

Dijkstra演算法是處理單源最短路徑的有效演算法,但它侷限於邊的權值非負的情況,若圖中出現權值為負的邊,Dijkstra演算法就會失效,求出的最短路徑就可能是錯的。

這時候,就需要使用其他的演算法來求解最短路徑,Bellman-Ford演算法就是其中最常用的一個。該演算法由美國數學家理查德貝爾曼(Richard Bellman, 動態規劃的提出者)和小萊斯特福特(Lester Ford)發明。

適用條件&範圍:

單源最短路徑(從源點s到其它所有頂點v);

有向圖&無向圖(無向圖可以看作(u,v),(v,u)同屬於邊集E的有向圖);

邊權可正可負(如有負權迴路輸出錯誤提示

);

差分約束系統;

Bellman-Ford演算法的流程如下:
給定圖G(V, E)(其中VE分別為圖G的頂點集與邊集),源點s陣列Distant[i]記錄從源點s到頂點i的路徑長度,初始化陣列Distant[n], Distant[s]0

以下操作迴圈執行至多n-1次,n為頂點數:
對於每一條邊e(u, v),如果Distant[u] + w(u, v) < Distant[v],則另Distant[v] = Distant[u]+w(u, v)w(u, v)為邊e(u,v)的權值;
若上述操作沒有對Distant進行更新,說明最短路徑已經查詢完畢,或者部分點不可達,跳出迴圈。否則執行下次迴圈;

為了檢測圖中是否存在負環路,即權值之和小於0的環路。對於每一條邊e(u, v),如果存在Distant[u] + w(u, v) < Distant[v]的邊,則圖中存在負環路,即是說改圖無法求出單源最短路徑。否則陣列Distant[n]中記錄的就是源點s到各頂點的最短路徑長度。

可知,Bellman-Ford演算法尋找單源最短路徑的時間複雜度為O(V*E).

BellmanFord演算法可以大致分為三個部分
第一,初始化所有點。每一個點儲存一個值,表示從原點到達這個點的距離,將原點的值設為0,其它的點的值設為無窮大(表示不可達)。
第二,進行迴圈,迴圈下標為從1n1n等於圖中點的個數)。在迴圈內部,遍歷所有的邊,進行鬆弛計算。


第三,遍歷途中所有的邊(edgeuv)),判斷是否存在這樣情況:
dv) > d (u) + w(u,v)
則返回false,表示途中存在從源點可達的權為負的迴路。
 
之所以需要第三部分的原因,是因為,如果存在從源點可達的權為負的迴路。則 應為無法收斂而導致不能求出最短路徑。 




程式程式碼:

/*
 * bellman_ford.cc
 *
 *  Created on: Jan 5, 2013
 *      Author: guixl
 */


#include <stdio.h>
#include <string.h>

const int MAX_VERTEX = 100;
const int MAX_EDGE = 100;
const int INF = 1<<30;

int u[MAX_EDGE]; //Edge start point array
int v[MAX_EDGE]; //Edge stop point array
int w[MAX_EDGE]; //Edge weight array

int d[MAX_VERTEX];
int prefix[MAX_VERTEX];
int vertex_number=6;
int edge_number=9;

void BuildGraph() {
	u[0] = 0; v[0]=1; w[0]=1;
	u[1] = 0; v[1]=2; w[1]=6;
	u[2] = 0; v[2]=5; w[2]=10;
	u[3] = 1; v[3]=3; w[3]=2;
	u[4] = 1; v[4]=4; w[4]=1;
	u[5] = 2; v[5]=3; w[5]=4;
	u[6] = 2; v[6]=4; w[6]=5;
	u[7] = 3; v[7]=5; w[7]=3;
	u[8] = 4; v[8]=5; w[8]=1;
}

void BellmanFord() {
	for (int i=0; i<vertex_number; i++)
		d[i] = i==0 ? 0 : INF;

	for (int i=0; i<vertex_number; i++) {
		for (int j=0; j<edge_number; j++) {
			int start = u[j];
			int stop = v[j];
			if (d[start] < INF && d[start] + w[j] < d[stop]) {
				d[stop] = d[start] + w[j];
				prefix[stop] = start;
			}
		}
	}
}

void Print() {
	for (int i=0; i<vertex_number; i++) {
		printf("Shortest path from 0 to %d is %d:\n", i, d[i]);
		for (int s=i; s!=0; s=prefix[s]) {
			printf("%d->%d\n", prefix[s], s);
		}
	}
}

int main(int argc, char** argv) {
	BuildGraph();
	BellmanFord();
	Print();
	return 0;
}