作为一种单源最短路径算法,Bellman-Ford对于有向图和无向图都能适用,它还有一个Dijkstra算法无法具备的特点,那就是对含负权图的最短路径搜索。
每i轮对边的遍历之后,只要不存在负权回路,Bellman-Ford算法都可以保证获得距离源点i条边的点的最短路径。因为最短路径的最大长度不会超过n条边,n为节点的数目。所以n次遍历后所有的节点必定都能找到最短路径。如果n次后还可以继续松弛,则表明该图存在负权回路,可以对代码稍作修改来计算负权环的大小,此处不再做说明。
Bellman-Ford算法的流程如下:
给定图G(V, E)(其中V、E分别为图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).
/*
*贝尔曼福特算法-解决单元最短路径问题
*对于带负权的情况同样适用
*通过松弛操作来进行长度缩短
*/
#include<iostream>
using namespace std;
#define IniDis 88888888 //初始各个点到起始点的距离
struct Edge //边的结构体,包括起始点和目的点以及边的权值
{
int from;
int to;
int cost;
};
int *Distance = NULL; //各个点到起始点的距离
int *pre = NULL; //定义某个路径上某个点的前一个点的编号
//主题算法函数,参数为边信息,边数目,点数目,起始点
int Bellman_ford(Edge *edgeInfo, int nEdgeNum,int nNodeNum, int startPoint)
{
Distance = (int *) malloc(2*nNodeNum*sizeof(int));
pre = (int*)malloc(2*nNodeNum*sizeof(int));
if (Distance == NULL || pre == NULL)
{
return -1;
}
//初始化所有节点到初始节点的距离
memset(Distance, IniDis, 2*nNodeNum*sizeof(int));
Distance[startPoint] = 0;
for (int i = 1; i< nNodeNum+1; i++)
{
for (int j = 0; j < nEdgeNum; j++)
{
if (Distance[edgeInfo[j].from] + edgeInfo[j].cost < Distance[edgeInfo[j].to]) //新路径比老路径更短
{
Distance[edgeInfo[j].to] = Distance[edgeInfo[j].from] + edgeInfo[j].cost; //松弛操作
pre[edgeInfo[j].to] = edgeInfo[j].from; //更新来源点
}
}
}
for (int k =1; k< nNodeNum+1; k++)
{
if (Distance[edgeInfo[k].from] + edgeInfo[k].cost < Distance[edgeInfo[k].to])
{
return -2; //代表有负权回路
}
}
return 0;
}
//打印源点到各个点的信息
void PrintRoad(int nNodeNum)
{
for (int i = 1; i< nNodeNum+1; i++)
{
cout<<"源点距离点"<<i<<"的路径长度为:"<<Distance[i]<<endl;
}
}
int main()
{
int nodenum,edgenum,startpoint;
int flag = 0;
Edge *pEdgeInfo = NULL; //存储边信息的指针数组
cout<<"输入图的节点数目,边数目和起始点编号"<<endl;
cin>>nodenum>>edgenum>>startpoint;
pEdgeInfo = (Edge *)malloc(edgenum*sizeof(Edge));
if (pEdgeInfo == NULL)
{
cout<<"分配失败,退出"<<endl;
return -1;
}
//为各条边添加初始信息
for (int i = 0; i< edgenum; i++)
{
cin>>pEdgeInfo[i].from >>pEdgeInfo[i].to >>pEdgeInfo[i].cost;
}
flag = Bellman_ford(pEdgeInfo, edgenum,nodenum, startpoint);
if (flag == -1)
{
cout<<"初始化失败"<<endl;
}
else if (flag == -2)
{
cout<<"路径有负权回路"<<endl;
}
else
{
PrintRoad(nodenum);
}
free(pEdgeInfo);
free(Distance);
free(pre);
system("pause");
return 0;
}
测试数据:
4 6 1
1 2 20
1 3 5
4 1 -200
2 4 4
4 2 4
3 4 2
和:
4 6 1
1 2 2
1 3 5
4 1 10
2 4 4
4 2 4
3 4 2