最短路径——Bellman-Ford算法
一、相关定义
最短路径:求源点到某特定点的最短距离
特点:Bellman-Ford算法主要是针对有负权值的图,来判断该图中是否有负权回路或者存在最短路径的点
局限性:算法效率不高,不如SPFA算法
时间复杂度:O(mn)
【具体与dijkstra算法的比较】
Bellman-Ford算法为何需要循环n-1次来求解最短路径?Dijkstra从源点开始,更新dis[],找到最小值,再更新dis[]……每次循环都可以确定一个点的最短路。Bellman-Ford算法同样也是这样,它的每次循环也可以确定一个点的最短路,只不过代价很大,因为 Bellman-Ford每次循环都是操作所有边。既然代价这么大,相比Dijkstra 算法,Bellman-Ford算法还有啥用?因为后者可以检测负权回路啊。Bellman-Ford 算法的时间复杂度为 O(nm),其中 n 为顶点数,m 为边数。
【负权回路】
开始不懂,看了下面的图和我的算法描述后就懂了:


在循环n-1次的基础上再次遍历各边,对于所有边,只要存在一条边e(u, v)使得 dis[u] + w(u,v) < dis[v],则该图存在负权回路。
【松弛操作】


如左图所示,松弛计算之前,点B的值是8,但是点A的值加上边上的权重2,得到5,比点B的值(8)小,所以,点B的值减小为5。这个过程的意义是,找到了一条通向B点更短的路线,且该路线是先经过点A,然后通过权重为2的边,到达点B。
当然,如果出现右边这种情况,则不会修改点B的值,因为3+4>6。
二、算法描述
关键词:初始化 松弛操作
主要变量如下:
int n 表示有n个点,从1~n标号
int s,t s为源点,t为终点
int dis[N] dis[i]表示源点s到点i的最短路径
int pre[N] 记录路径,pre[i]表示i的前驱结点
bool vis[N] vis[i]=true表示点i被标记
【初始化】
dis数组全部赋值为INF,pre数组全部赋值为-1(表示还不知道前驱),
dis[s] = 0 表示源点不要求最短路径(或者最短路径就是0)。
【松弛操作】
对于每一条边e(u, v),如果dis[u] + w(u, v) < dis[v],则另dis[v] = dis[u]+w(u, v)。w(u, v)为边e(u,v)的权值
注:上述循环执行至多n-1次。若上述操作没有对Distant进行更新,说明最短路径已经查找完毕或者部分点不可达,跳出循环;否则执行下次循环。
【检测负权回路】
为了检测图中是否存在负环路,即权值之和小于0的环路。
检测的方法很简单,只需在求解最短路径的 n-1 次循环基础上,再进行第 n 次循环:对于每一条边e(u, v),如果存在边使得dis[u] + w(u, v) < dis[v],则图中存在负环路,即是说改图无法求出单源最短路径。否则数组Distant[n]中记录的就是源点s到各顶点的最短路径长度。
【小结】
Bellman-Ford算法可以大致分为三个部分:
- 初始化所有点。每一个点保存一个值,表示从原点到达这个点的距离,将原点的值设为0,其它的点的值设为无穷大(表示不可达)。
- 进行循环,循环下标为从1到n-1(n等于图中点的个数)。在循环内部,遍历所有的边,进行松弛计算。
- 遍历途中所有的边(edge(u,v)),判断是否存在这样情况: d(v) > d (u) + w(u,v),若存在,则返回false,表示图中存在从源点可达的权为负的回路。
之所以需要第三步的原因,是因为,如果存在从源点可达的权为负的回路,则将因为无法收敛而导致不能求出最短路径。
三、代码实现
#include<iostream>
#include<stack>
using namespace std; #define MAX 10000 //假设权值最大不超过10000 struct Edge
{
int u;
int v;
int w;
}; Edge edge[10000]; //记录所有边
int dist[100]; //源点到顶点i的最短距离
int path[100]; //记录最短路的路径
int vertex_num; //顶点数
int edge_num; //边数
int source; //源点 bool BellmanFord()
{
//初始化
for (int i = 0; i < vertex_num; i++)
dist[i] = (i == source) ? 0 : MAX; //n-1次循环求最短路径
for (int i = 1; i <= vertex_num - 1; i++)
{
for (int j = 0; j < edge_num; j++)
{
if (dist[edge[j].v] > dist[edge[j].u] + edge[j].w)
{
dist[edge[j].v] = dist[edge[j].u] + edge[j].w;
path[edge[j].v] = edge[j].u;
}
}
} bool flag = true; //标记是否有负权回路 //第n次循环判断负权回路
for (int i = 0; i < edge_num; i++)
{
if (dist[edge[i].v] > dist[edge[i].u] + edge[i].w)
{
flag = false;
break;
}
} return flag;
} void Print()
{
for (int i = 0; i < vertex_num; i++)
{
if (i != source)
{
int p = i;
stack<int> s;
cout << "顶点 " << source << " 到顶点 " << p << " 的最短路径是: "; while (source != p) //路径顺序是逆向的,所以先保存到栈
{
s.push(p);
p = path[p];
} cout << source;
while (!s.empty()) //依次从栈中取出的才是正序路径
{
cout << "--" << s.top();
s.pop();
}
cout << " 最短路径长度是:" << dist[i] << endl;
} }
} int main()
{ cout << "请输入图的顶点数,边数,源点:";
cin >> vertex_num >> edge_num >> source; cout << "请输入" << edge_num << "条边的信息:\n";
for (int i = 0; i < edge_num; i++)
cin >> edge[i].u >> edge[i].v >> edge[i].w; if (BellmanFord())
Print();
else
cout << "Sorry,it have negative circle!\n"; return 0;
}
运行截图:


最短路径——Bellman-Ford算法的更多相关文章
- Bellman - Ford 算法解决最短路径问题
Bellman - Ford 算法: 一:基本算法 对于单源最短路径问题,上一篇文章中介绍了 Dijkstra 算法,但是由于 Dijkstra 算法局限于解决非负权的最短路径问题,对于带负权的图就力 ...
- Bellman—Ford算法思想
---恢复内容开始--- Bellman—Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题.对于给定的带权(有向或无向)图G=(V,E),其源点为s,加权函数w是边集E的映射.对图G ...
- ACM/ICPC 之 最短路径-Bellman Ford范例(POJ1556-POJ2240)
两道Bellman Ford解最短路的范例,Bellman Ford只是一种最短路的方法,两道都可以用dijkstra, SPFA做. Bellman Ford解法是将每条边遍历一次,遍历一次所有边可 ...
- Dijkstra算法与Bellman - Ford算法示例(源自网上大牛的博客)【图论】
题意:题目大意:有N个点,给出从a点到b点的距离,当然a和b是互相可以抵达的,问从1到n的最短距离 poj2387 Description Bessie is out in the field and ...
- poj1860 bellman—ford队列优化 Currency Exchange
Currency Exchange Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 22123 Accepted: 799 ...
- uva 558 - Wormholes(Bellman Ford判断负环)
题目链接:558 - Wormholes 题目大意:给出n和m,表示有n个点,然后给出m条边,然后判断给出的有向图中是否存在负环. 解题思路:利用Bellman Ford算法,若进行第n次松弛时,还能 ...
- 单源最短路径(dijkstra算法)php实现
做一个医学项目,当中在病例评分时会用到单源最短路径的算法.单源最短路径的dijkstra算法的思路例如以下: 如果存在一条从i到j的最短路径(Vi.....Vk,Vj),Vk是Vj前面的一顶点.那么( ...
- 最大流算法之EK(最短路径增广算法)
这是网络流最基础的部分--求出源点到汇点的最大流(Max-Flow). 最大流的算法有比较多,本次介绍的是其中复杂度较高,但是比较好写的EK算法.(不涉及分层,纯粹靠BFS找汇点及回溯找最小流量得到最 ...
- 【算法设计与分析基础】25、单起点最短路径的dijkstra算法
首先看看这换个数据图 邻接矩阵 dijkstra算法的寻找最短路径的核心就是对于这个节点的数据结构的设计 1.节点中保存有已经加入最短路径的集合中到当前节点的最短路径的节点 2.从起点经过或者不经过 ...
- 最短路径问题---Dijkstra算法详解
侵删https://blog.csdn.net/qq_35644234/article/details/60870719 前言 Nobody can go back and start a new b ...
随机推荐
- Django-rest-framework(五)自定义功能
我们可以在settings.py文件中定义登录,权限,分页,异常等的全局配置,如下所示 REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'utils.pa ...
- Struts2 第三讲 -- Struts2的处理流程
4.Struts2的处理流程 以下是struts-defautl.xml中的拦截器 建议通过这个struts-default的副本查看,更形象 它实现了很多的功能,其中包括国际化,文件上传,类型转换, ...
- Oracle四舍五入,向上取整,向下取整
用oracle sql对数字进行操作: 取上取整.向下取整.保留N位小数.四舍五入.数字格式化 取整(向下取整): select floor(5.534) from dual; select trun ...
- File zilla远程连接服务器报错:服务器发回了不可路由的地址,使用服务器地址代替
百度的答案都是:更改Filezilla设置,编辑-设置-连接-FTP-被动模式,将“使用服务器的外部ip地址来代替”改为“回到主动模式”即可.但问题没有解决!!! 由于使用的是阿里云的服务器.安全组里 ...
- SQL3120W 不能将xx的字段值转换成 INTEGER值
一次用DB2 Load/Import导入数据时,报错,提示SQL3120W 不能将xx的字段值转换成 INTEGER值,但目标列不可为空.未装入该行. 目标表: CREATE TABLE TEST( ...
- zTree的核心处理逻辑
zTree 是一个前端树形结构的插件. 使用起来很简单,我们重点关注一下插件的核心代码. 首先,zTree需要如下的数据结构: let areaData = [ { "id": & ...
- A^B Mod C (51Nod - 1046 )(快速幂)
给出3个正整数A B C,求A^B Mod C. 例如,3 5 8,3^5 Mod 8 = 3. Input 3个正整数A B C,中间用空格分隔.(1 <= A,B,C <= 10^ ...
- 富文本编辑器 summernote.js
1.引用js 可在 https://summernote.org/ 官网下载 ,并查看详细的API 引入:summernote.js 和 summernote-zh-CN.js 以及样式文件:su ...
- 表单验证(JQ)
<!DOCTYPE html> <html> <head> <title></title> <meta charset="u ...
- mac制作U盘启动器
Infi-chu: http://www.cnblogs.com/Infi-chu/ 一.所需工具及必要条件: 1. 首先需要一个大于16GB U盘. 2.电脑系统版本应该大于10.11.X(因为之前 ...