最短路径——SPFA算法
一、前提引入
我们学过了Bellman-Ford算法,现在又要提出这个SPFA算法,为什么呢?
考虑一个随机图(点和边随机生成),除了已确定最短路的顶点与尚未确定最短路的顶点之间的边,其它的边所做的都是无用的,大致描述为下图(分割线以左为已确定最短路的顶点):
其中红色部分为所做无用的边,蓝色部分为实际有用的边。既然只需用到中间蓝色部分的边,那就是SPFA算法的优势之处了。
二、算法描述
算法特点:在 Bellman-ford 算法的基础上加上一个队列优化,减少了冗余的松弛操作,是一种高效的最短路算法。
时间复杂度:O(mn)
关键词:初始化 松弛操作 队列
主要变量如下:
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在队列中
queue<int> q 队列,在整个算法中有顶点入队了要记得标记vis数组,有顶点出队了记得消除那个标记
【初始化】
dis数组全部赋值为INF,pre数组全部赋值为-1(表示还不知道前驱),
dis[s] = 0 表示源点不要求最短路径(或者最短路径就是0)。
【队列+松弛操作】
读取队头顶点u,并将队头顶点u出队(记得消除标记);将与点u相连的所有点v进行松弛操作,如果能更新估计值(即令d[v]变小),那么就更新,另外,如果点v没有在队列中,那么要将点v入队(记得标记),如果已经在队列中了,那么就不用入队,这样不断从队列中取出顶点来进行松弛操作。
以此循环,直到队空为止就完成了单源最短路的求解。
【算法过程】
设立一个队列用来保存待优化的顶点,优化时每次取出队首顶点 u,并且用 u 点当前的最短路径估计值dis[u]
对与 u 点邻接的顶点 v 进行松弛操作,如果 v 点的最短路径估计值dis[v]
可以更小,且 v 点不在当前的队列中,就将 v 点放入队尾。这样不断从队列中取出顶点来进行松弛操作,直至队列空为止。
【检测负权回路】
方法:如果某个点进入队列的次数大于等于 n,则存在负权回路,其中 n 为图的顶点数。
说明:SPFA无法处理带负环的图。
三、代码实现
- #include<iostream>
- #include<queue>
- #include<stack>
- using namespace std;
- int matrix[100][100]; //邻接矩阵
- bool visited[100]; //标记数组
- int dist[100]; //源点到顶点i的最短距离
- int path[100]; //记录最短路的路径
- int enqueue_num[100]; //记录入队次数
- int vertex_num; //顶点数
- int edge_num; //边数
- int source; //源点
- bool SPFA()
- {
- memset(visited, 0, sizeof(visited));
- memset(enqueue_num, 0, sizeof(enqueue_num));
- for (int i = 0; i < vertex_num; i++)
- {
- dist[i] = INT_MAX;
- path[i] = source;
- }
- queue<int> Q;
- Q.push(source);
- dist[source] = 0;
- visited[source] = 1;
- enqueue_num[source]++;
- while (!Q.empty())
- {
- int u = Q.front();
- Q.pop();
- visited[u] = 0;
- for (int v = 0; v < vertex_num; v++)
- {
- if (matrix[u][v] != INT_MAX) //u与v直接邻接
- {
- if (dist[u] + matrix[u][v] < dist[v])
- {
- dist[v] = dist[u] + matrix[u][v];
- path[v] = u;
- if (!visited[v])
- {
- Q.push(v);
- enqueue_num[v]++;
- if (enqueue_num[v] >= vertex_num)
- return false;
- visited[v] = 1;
- }
- }
- }
- }
- }
- return true;
- }
- 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;
- for (int i = 0; i < vertex_num; i++)
- for (int j = 0; j < vertex_num; j++)
- matrix[i][j] = INT_MAX; //初始化matrix数组
- cout << "请输入" << edge_num << "条边的信息:\n";
- int u, v, w;
- for (int i = 0; i < edge_num; i++)
- {
- cin >> u >> v >> w;
- matrix[u][v] = w;
- }
- if (SPFA())
- Print();
- else
- cout << "Sorry,it have negative circle!\n";
- return 0;
- }
运行如下:
最短路径——SPFA算法的更多相关文章
- 最短路径--SPFA 算法
适用范围:给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了. 我们约定有向加权图G不存在负权回路,即最短路径一 ...
- 图的最短路径-----------SPFA算法详解(TjuOj2831_Wormholes)
这次整理了一下SPFA算法,首先相比Dijkstra算法,SPFA可以处理带有负权变的图.(个人认为原因是SPFA在进行松弛操作时可以对某一条边重复进行松弛,如果存在负权边,在多次松弛某边时可以更新该 ...
- 最短路径----SPFA算法
求最短路径的算法有许多种,除了排序外,恐怕是ACM界中解决同一类问题算法最多的了.最熟悉的无疑是Dijkstra,接着是Bellman-Ford,它们都可以求出由一个源点向其他各点的最短路径:如果我们 ...
- 最短路径SPFA算法(邻接表存法)
queue <int> Q; void SPFA (int s) { int i, v; for(int i=0; i<=n; i++) dist[i]=INF; //初始化每点i到 ...
- 洛谷P3371单源最短路径SPFA算法
SPFA同样是一种基于贪心的算法,看过之前一篇blog的读者应该可以发现,SPFA和堆优化版的Dijkstra如此的相似,没错,但SPFA有一优点是Dijkstra没有的,就是它可以处理负边的情况. ...
- 最短路径问题的Dijkstra和SPFA算法总结
Dijkstra算法: 解决带非负权重图的单元最短路径问题.时间复杂度为O(V*V+E) 算法精髓:维持一组节点集合S,从源节点到该集合中的点的最短路径已被找到,算法重复从剩余的节点集V-S中选择最短 ...
- Bellman-Ford & SPFA 算法——求解单源点最短路径问题
Bellman-Ford算法与另一个非常著名的Dijkstra算法一样,用于求解单源点最短路径问题.Bellman-ford算法除了可求解边权均非负的问题外,还可以解决存在负权边的问题(意义是什么,好 ...
- 最短路径算法之四——SPFA算法
SPAF算法 求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm,该算法是西南交通大学段凡丁于1994年发表的. 它可以在O(kE)的时间复杂度内求出源点 ...
- 数据结构与算法--最短路径之Bellman算法、SPFA算法
数据结构与算法--最短路径之Bellman算法.SPFA算法 除了Floyd算法,另外一个使用广泛且可以处理负权边的是Bellman-Ford算法. Bellman-Ford算法 假设某个图有V个顶点 ...
随机推荐
- Android在代码中设置控件的drawableLeft,drawableRight,drawableTop,drawableBottom。
根据业务的需要,要在代码中设置控件的drawableLeft,drawableRight,drawableTop,drawableBottom属性. 我们知道在xml中设置的方法为:android:d ...
- 推荐几本对于Python初学者比较好的书籍(内含PDF)
我们提供一个初学者最好的Python书籍列表.Python是一个初级程序员可以学习编程的最友好语言之一.为了帮助您开始使用Python编程,我们分享此列表.泡一杯茶,选一本书阅读,开始使用Python ...
- How to Enable TLS 1.2 on Windows Server 2008 R2 and IIS 7.5
Nowadays there is an SSL vulnerability called POODLE discovered by Google team in SSLv3 protocol. So ...
- Luogu P3177 [HAOI2015]树上染色
一道有机结合了计数和贪心这一DP两大考点的神仙题,不得不说做法是很玄妙. 首先我们很容易想到DP,设\(f_{i,j}\)表示在以\(i\)为根节点的子树中选\(j\)个黑色节点的最大收益值. 然后我 ...
- Luogu P2597 [ZJOI2012]灾难
一道非常综合的好题然后就莫名其妙地知道了动态LCA的求法 果然是ZJOI的题目,只能说这思路服了 首先我们发现每次操作只会灭绝一种动物,然后我们想一下就知道如果有\(n(n>=2)\)个食物的动 ...
- 【Java并发.1】简介
继上一本<深入理解Java虚拟机>之后,学习计划里的另一本书<Java并发编程实战>现在开始学习,并记录学习笔记. 第一章主要内容是介绍 并发 的简介.发展.特点. 编写正确的 ...
- [UWP 自定义控件]了解模板化控件(3):实现HeaderedContentControl
1. 概述 来看看这段XMAL: <StackPanel Width="300"> <TextBox Header="TextBox" /&g ...
- python爬虫xpath的语法
有朋友问我正则,,okey,其实我的正则也不好,但是python下xpath是相对较简单的 简单了解一下xpath: XPath 是一门在 XML 文档中查找信息的语言.XPath 可用来在 XML ...
- TRIO-basic指令--FLEXLINK
Type: Axis Command Syntax: FLEXLINK(base_dist, excite_dist, link_dist, base_in, base_out, excite_acc ...
- Windows10 家庭版 Docker的安装
Docker的安装 1.简介:Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中, 然后发布到任何流行的 Linux 机器上,也可以实现虚拟化.容器是完全 ...