几点说明:
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;
}