SPFA单源最短路径算法
我们用数组d记录每个结点的最短路径估计值,而且用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。
如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。
期望时间复杂度:O(me), 其中m为所有顶点进队的平均次数,可以证明m一般小于等于2:“算法编程后实际运算情况表明m一般没有超过2n.事实上顶点入队次数m是一个不容易事先分析出来的数,但它确是一个随图的不同而略有不同的常数.所谓常数,就是与e无关,与n也无关,仅与边的权值分布有关.一旦图确定,权值确定,原点确定,m就是一个确定的常数.所以SPFA算法复杂度为O(e).证毕."(SPFA的论文)不过,这个证明是非常不严谨甚至错误的,事实上在bellman算法的论文中已有这方面的内容,所以国际上一般不承认SPFA算法。
#include<iostream>
#include<vector>
#include<deque>
using namespace std;
struct Edge
{
int to,length;
};
bool spfa(const int &beg,//出发点
const vector<vector<Edge> > &adjlist,//邻接表,通过传引用避免拷贝
vector<int> &dist,//出发点到各点的最短路径长度
vector<int> &path)//路径上到达该点的前一个点
//C++习惯上函数异常返回非零值,未异常才返回0(想想main函数),因此出现负权回路返回1!
//福利:这个函数没有调用任何全局变量,可以直接复制!
{
const int &INF=0x7FFFFFFF,&NODE=adjlist.size();//用邻接表的大小传递顶点个数,减少参数传递
dist.assign(NODE,INF);//初始化距离为无穷大
path.assign(NODE,-);//初始化路径为未知
deque<int> que(,beg);//(双端)处理队列
vector<bool> flag(NODE,);//标志数组,判断是否在队列中
vector<int> cnt(NODE,);//记录各点入队次数,用于判断负权回路
dist[beg]=;//出发点到自身路径长度为0
++cnt[beg];//开始计数
flag[beg]=;//入队
while(!que.empty())
{
const int now=que.front();//当前处理的点,由于后面被删除,不可定义成常量引用
que.pop_front();
flag[now]=;//将该点拿出队列
for(int i=; i!=adjlist[now].size(); ++i)//遍历所有与当前点有路径的点
{
const int &next=adjlist[now][i].to;//目标点,不妨定义成常量引用,稍稍快些
if(dist[now]<INF&&//若距离已知(否则下面右式计算结果必爆int),且
//注:与运算先判断左式是否成立,若不成立则右式不会被判断
dist[next]>dist[now]+adjlist[now][i].length)//优于当前值
{
dist[next]=dist[now]+adjlist[now][i].length;//更新
path[next]=now;//记录路径
if(!flag[next])//若未在处理队列中
{
if(++cnt[next]==NODE)return ;//计数后出现负权回路
if(que.empty()||//空队列,或(或运算实现原理类似与运算)
dist[next]<dist[que.front()])//优先级高于队首(SLF)
que.push_front(next);//放在队首
else que.push_back(next);//否则放在队尾
flag[next]=;//入队
}
}
}
}
return ;
}
int main()
{
int n_num,e_num,beg;//含义见下
cout<<"输入点数、边数、出发点:";
cin>>n_num>>e_num>>beg;
vector<vector<Edge> > adjlist(n_num,vector<Edge>());//默认初始化邻接表
for(int i=,p; i!=e_num; ++i)
{
Edge tmp;
cout<<"输入第"<<i+<<"条边的起点、终点、长度:";
cin>>p>>tmp.to>>tmp.length;
adjlist[p].push_back(tmp);
}
vector<int> dist,path;//用于接收最短路径长度及路径各点
if(spfa(beg,adjlist,dist,path))cout<<"图中存在负权回路\n";
else for(int i=; i!=n_num; ++i)
{
cout<<beg<<"到"<<i<<"的最短距离为"<<dist[i]<<",反向打印路径:";
for(int w=i; path[w]>=; w=path[w])
cout<<w<<"<-";
cout<<beg<<'\n';
}
}
SPFA单源最短路径算法的更多相关文章
- Dijkstra 单源最短路径算法
Dijkstra 算法是一种用于计算带权有向图中单源最短路径(SSSP:Single-Source Shortest Path)的算法,由计算机科学家 Edsger Dijkstra 于 1956 年 ...
- Bellman-Ford 单源最短路径算法
Bellman-Ford 算法是一种用于计算带权有向图中单源最短路径(SSSP:Single-Source Shortest Path)的算法.该算法由 Richard Bellman 和 Leste ...
- 经典贪心算法(哈夫曼算法,Dijstra单源最短路径算法,最小费用最大流)
哈夫曼编码与哈夫曼算法 哈弗曼编码的目的是,如何用更短的bit来编码数据. 通过变长编码压缩编码长度.我们知道普通的编码都是定长的,比如常用的ASCII编码,每个字符都是8个bit.但在很多情况下,数 ...
- 单源最短路径算法:迪杰斯特拉 (Dijkstra) 算法(二)
一.基于邻接表的Dijkstra算法 如前一篇文章所述,在 Dijkstra 的算法中,维护了两组,一组包含已经包含在最短路径树中的顶点列表,另一组包含尚未包含的顶点.使用邻接表表示,可以使用 BFS ...
- 单源最短路径算法:迪杰斯特拉 (Dijkstra) 算法(一)
一.算法介绍 迪杰斯特拉算法(英语:Dijkstra's algorithm)由荷兰计算机科学家艾兹赫尔·迪杰斯特拉在1956年提出.迪杰斯特拉算法使用了广度优先搜索解决赋权有向图的单源最短路径问题. ...
- 单源最短路径算法---Dijkstra
Dijkstra算法树解决有向图G=(V,E)上带权的单源最短路径问题,但是要求所有边的权值非负. 解题思路: V表示有向图的所有顶点集合,S表示那么一些顶点结合,从源点s到该集合中的顶点的最终最短路 ...
- Dijkstra算法详细(单源最短路径算法)
介绍 对于dijkstra算法,很多人可能感觉熟悉而又陌生,可能大部分人比较了解bfs和dfs,而对dijkstra和floyd算法可能知道大概是图论中的某个算法,但是可能不清楚其中的作用和原理,又或 ...
- 单源最短路径算法——Dijkstra算法(迪杰斯特拉算法)
一 综述 Dijkstra算法(迪杰斯特拉算法)主要是用于求解有向图中单源最短路径问题.其本质是基于贪心策略的(具体见下文).其基本原理如下: (1)初始化:集合vertex_set初始为{sourc ...
- 单源最短路径算法——Bellman-ford算法和Dijkstra算法
BellMan-ford算法描述 1.初始化:将除源点外的所有顶点的最短距离估计值 dist[v] ← +∞, dist[s] ←0; 2.迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V ...
随机推荐
- Python入门之面向对象的多态
本章目录: 一.多态 二.多态性 三.鸭子类型 ============================== 一.多态 多态指的是一类事物有多种形态. 动物有多种形态:人,狗,猪. import ab ...
- SQL去重之distinct和group by的应用
遇到一个需求,要去重查出某张表的字段一和字段二,但是查出来的结果要按照表中记录的创建时间排序. 于是,第一时间就想到了使用distinct这个去重专用语法了: select distinct col1 ...
- Confluence5.8协作平台软件安装(Linux)
Confluence5.8协作平台软件安装(Linux) 一.简介 Confluence是一个专业的企业知识管理与协同软件,也可以用于构建企业wiki.使用简单,但它强大的编辑和站点管理特征能够帮助团 ...
- windows的gvim总是报错: +iconv fencview.vim
iconv是用来转换gvim文件的编码的, 需要插件: iconv.dll gvim7.3的文件目录结构: vim/vim73是它的核心文件, 而vimfiles是扩展文件, 里面的plugin是专门 ...
- bootstrap5
列表组的使用 ul.list-group > li.list-group-item *5... 列表组中可以放置徽标: 在li中放置 span.badge. bootstrap中的情景类: 实际 ...
- newcode wyh的吃鸡(优势队列+BFS)题解
思路: 要用优势队列,因为有的+2,有的+1,所以队列中的步长是不单调的,所以找到一个答案但不一定最小,所以用优势队列把小的放在队首. 要记录状态,所以开了三维,题目和昨天做的那道小明差不多 vis开 ...
- perl 入门知识(1)
<一> 语句及注释: Perl 语句以分号(;)结尾,用 # 作为一行的注释,没有其它语言中那种跨行的注释.代码块用大括号围起来,这个和 C 类似,但这个大括号在有些地方是强制要求,如在 ...
- BZOJ1355: [Baltic2009]Radio Transmission KMP
Description 给你一个字符串,它是由某个字符串不断自我连接形成的. 但是这个字符串是不确定的,现在只想知道它的最短长度是多少. Input 第一行给出字符串的长度,1 < L ≤ 1, ...
- java代码实现highchart与数据库数据结合完整案例分析(一)---饼状图
作者原创:转载请注明出处 在做项目的过程中,经常会用到统计数据,同时会用到highchart或echart进行数据展示,highchart是外国开发的数据统计图插件, echart是我们国家开发的数据 ...
- java中集合格式及json格式的特点和转换
作者原创:转载请注明出处 今天在写代码,遇到一个难点,由于要调用webservice接口,返回的为一个list集合内容,从webservice调用接口返回的为一个string的io流, 在调用接口的地 ...