幾點說明:
1、實現參考了《算法導論》中的單源最短路徑的章節中的僞代碼。嚴格證明、以及深入學習請參考書籍。
2、Bellman-Ford最短路徑算法支持權值爲負值,且可以檢測是否存在權重爲負值的環路。
3、相比Bellman-Ford,還有其它適用於不同變體的單源最短路徑算法,譬如迪傑斯特拉(Dijkstra)最短路徑算法,後者適用於權值爲非負數的圖,且時間複雜度更低。同樣深入瞭解請參考《算法導論》。
3、簡單算法說明和輸入,見下圖
#include <math.h>
#include <iostream>
void relax(double *dist, double ** weight, int *prev, int u, int v)
{
if (weight[u][v] < 0 || dist[u] < 0)
{
return;
}
if (dist[v] > dist[u] + weight[u][v])
{
dist[v] = dist[u] + weight[u][v];
prev[v] = u;
}
}
/**
* @brief bellman_ford單源最短路徑
* @param N [IN] 頂點的個數
* @param s [IN] 源點的索引
* @param weight [IN] 有向圖權值表;如果爲無向圖,則雙向邊的權值對稱即可
* @param prev [OUT] 輸出最短路徑,每個節點的前一個節點的索引;輸入空指針即可,結果需要外部使用delete[]釋放堆內存
* @return 是否存在權重爲負值的環路
*/
bool bellman_ford(int N, int s, double ** weight, int *&prev)
{
prev = new int[N];
double *dist = new double[N]; //最短距離值,根據需要可以作爲輸入輸出參數
for (int i = 0; i < N; i++)
{
dist[i] = HUGE_VALD; //初始化爲無窮大
prev[i] = -1; //初始化爲無效索引值
}
dist[s] = 0; //從節點s找到其他節點的最短路徑
for (int T = 0; T < N - 1; ++T)
{
for (int u = 0; u < N; u++)
{
for (int v = 0; v < N; v++)
{
if (u == v)
{
continue;
}
if (weight[u][v] > 0)
{
relax(dist, weight, prev, u, v);
}
}
}
}
//判斷是否存在權重爲負值的環路
for (int u = 0; u < N; u++)
{
for (int v = 0; v < N; v++)
{
if (u == v)
{
continue;
}
if (weight[u][v] > 0)
{
if (dist[v] > dist[u] + weight[u][v])
{
return false;
}
}
}
}
return true;
}
int main()
{
//1、構造5*5輸入
const double INF = 999999; //HUGE_VALD、DBL_MAX
double weight0[5][5] = {
INF, 10, INF, 5, INF,
INF, INF, 1, 2, INF,
INF, INF, INF, INF, 4,
INF, 3, 9, INF, 2,
7, INF, 6, INF, INF
};
int N = 5;
int *prev = NULL;
double **weight = new double*[N];
for (int i = 0; i < N; i++)
{
weight[i] = new double[N];
memcpy(weight[i], weight0[i], sizeof(double)*5);
}
//2、計算
bellman_ford(5, 0, weight, prev);
//3、輸出結果(每個節點的前一個節點的索引)
for (int i = 0; i < 5; i++)
{
std::cout << i << ".prev=" << prev[i] << std::endl;
}
// 0.prev=-1
// 1.prev=3
// 2.prev=1
// 3.prev=0
// 4.prev=3
//4、釋放堆內存
for (int i = 0; i < N; i++)
{
delete []weight[i];
}
delete []weight;
delete []prev;
return 0;
}