博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Bellman-Ford算法,SPFA算法
阅读量:4957 次
发布时间:2019-06-12

本文共 2592 字,大约阅读时间需要 8 分钟。

 

Bellman-Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题。对于给定的带权(有向或无向)图 G=V,E),其源点为s,加权函数 w 边集 E 的映射。对图G运行Bellman-Ford算法的结果是一个布尔值,表明图中是否存在着一个从源点s可达的负权回路。若不存在这样的回路,算法将给出从源点s G的任意顶点v的最短路径d[v]

Bellman-Ford算法流程分为三个阶段:

(1)    初始化:将除源点外的所有顶点的最短距离估计值 d[v] ←+∞, d[s] ←0;

(2)    迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)

(3)    检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在 d[v]中。

 

 

 

算法描述如下:

Bellman-Ford(G,w,s) boolean   //,边集 函数 w s为源点

1        for each vertex v ∈ V(G) do        //初始化 1阶段

2            d[v] ←+∞

3        d[s] ←0;                             //1阶段结束

4        for i=1 to |v|-1 do               //2阶段开始,双重循环。

5           for each edge(u,v) ∈E(G) do //边集数组要用到,穷举每条边。

6              If d[v]> d[u]+ w(u,v) then      //松弛判断

7                 d[v]=d[u]+w(u,v)               //松弛操作   2阶段结束

8        for each edge(u,v) ∈E(G) do

9            If d[v]> d[u]+ w(u,v) then

10            Exit false

11    Exit true

 

下面给出描述性证明:

   首先指出,图的任意一条最短路径既不能包含负权回路,也不会包含正权回路,因此它最多包含|v|-1条边。

   其次,从源点s可达的所有顶点如果 存在最短路径,则这些最短路径构成一个以s为根的最短路径树。Bellman-Ford算法的迭代松弛操作,实际上就是按顶点距离s的层次,逐层生成这棵最短路径树的过程。

在对每条边进行1遍松弛的时候,生成了从s出发,层次至多为1的那些树枝。也就是说,找到了与s至多有1条边相联的那些顶点的最短路径;对每条边进行第2遍松弛的时候,生成了第2层次的树枝,就是说找到了经过2条边相连的那些顶点的最短路径……。因为最短路径最多只包含|v|-1 条边,所以,只需要循环|v|-1 次。

每实施一次松弛操作,最短路径树上就会有一层顶点达到其最短距离,此后这层顶点的最短距离值就会一直保持不变,不再受后续松弛操作的影响。(但是,每次还要判断松弛,这里浪费了大量的时间,怎么优化?单纯的优化是否可行?)

如果没有负权回路,由于最短路径树的高度最多只能是|v|-1,所以最多经过|v|-1遍松弛操作后,所有从s可达的顶点必将求出最短距离。如果 d[v]仍保持 +∞,则表明从s到v不可达。

如果有负权回路,那么第 |v|-1 遍松弛操作仍然会成功,这时,负权回路上的顶点不会收敛。

 

 

/* * 单源最短路算法SPFA,时间复杂度O(kE),k在一般情况下不大于2,对于每个顶点使用可以在O(VE)的时间内算出每对节点之间的最短路 * 使用了队列,对于任意在队列中的点连着的点进行松弛,同时将不在队列中的连着的点入队,直到队空则算法结束,最短路求出 * SPFA是Bellman-Ford的优化版,可以处理有负权边的情况 * 对于负环,我们可以证明每个点入队次数不会超过V,所以我们可以记录每个点的入队次数,如果超过V则表示其出现负环,算法结束 * 由于要对点的每一条边进行枚举,故采用邻接表时时间复杂度为O(kE),采用矩阵时时间复杂度为O(kV^2) */#include
#include
#include
#define MAXV 10000#define INF 1000000000 //此处建议不要过大或过小,过大易导致运算时溢出,过小可能会被判定为真正的距离 using std::vector;using std::queue; struct Edge{
int v; //边权 int to; //连接的点}; vector
e[MAXV]; //由于一般情况下E<
buff; //队列,用于存储在SPFA算法中的需要松弛的节点bool done[MAXV]; //用于判断该节点是否已经在队列中int V; //节点数int E; //边数 bool spfa(const int st){
//返回值:TRUE为找到最短路返回,FALSE表示出现负环退出 for(int i=0;i
V){ //已经超过V次,出现负环 while(!buff.empty())buff.pop(); //清空队列,释放内存 return false; //返回FALSE } } } } buff.pop();//弹出队首节点 done[tmp]=0;//将队首节点标记为未入队 } return true; //返回TRUE} //算法结束 int main(){ //主函数 scanf("%d%d",&V,&E); //读入点数和边数 for(int i=0,x,y,l;i
y有一条有向边长度为l Edge tmp; //设置一个临时变量,以便存入vector tmp.v=l; //设置边权 tmp.to=y; //设置连接节点 e[x].push_back(tmp); //将这条边压入x的表中 } if(!spfa(0)){ //出现负环 printf("出现负环,最短路不存在\n"); }else{ //存在最短路 printf("节点0到节点%d的最短距离为%d",V-1,dist[V-1]); } return 0;}

转载于:https://www.cnblogs.com/gaiwen/articles/2962045.html

你可能感兴趣的文章
linux之查找文件,目录命令
查看>>
EXTJS信息提示框的注意事项
查看>>
POI Excel表格合并,边框设置
查看>>
CocoaPods 建立私有仓库
查看>>
ubuntu下code::blocks设置运行窗口为gnome命令行
查看>>
Web开发(XAMPP服务器搭建)
查看>>
vue2.0 实现click点击当前li,动态切换class
查看>>
java中equals方法和“==”的区别
查看>>
jQuery easing
查看>>
shell之使用cut切割文本文件
查看>>
基于Metronic的Bootstrap开发框架经验总结(3)--下拉列表Select2插件的使用
查看>>
撤销操作
查看>>
sscanf在字符串中的一些使用
查看>>
[转]new一个Object对象占用多少内存?
查看>>
一步步教你Hadoop多节点集群安装配置
查看>>
JS_轮播案例
查看>>
【转】STM32 - 程序跳转、中断、开关总中断
查看>>
== & ===
查看>>
详解C#中的反射
查看>>
给java初学发者的一些建议,并对自身一年做一个总结。
查看>>