最短路径——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个顶点 ...
随机推荐
- 树莓派学习笔记(6):让Raspbian支持中文、禁用休眠
引自:http://www.leiphone.com/news/201406/raspberry-pi-hands-on.html 一直令我很奇怪的是,即使你用Debian的安装光碟安装系统,如果你把 ...
- 浅谈UI自动化测试
最近一直在学习python,正好部门技术结构调整,就开始了点工向UI自动化测试的转变,我要说瞌睡来了就掉枕头么? 不过还好,可以将python的学习成果在自动化测试中实践... 1.about自动化测 ...
- JSON构造/解析(by C)---cJSON和json-c
背景 JSON即JavaScript Object Notation,是一种轻量级的数据交换格式. JSON建构于两种结构: "名称/值"对的集合(A collection of ...
- Luogu3825 NOI2017 游戏 2-SAT
传送门 第一眼看上去似乎是一个3-SAT问题 然而\(d \leq 8\)给我们的信息就是:暴力枚举 枚举\(x\)型地图变成\(a\)型地图还是\(b\)型地图(实际上不要枚举\(c\),因为\(a ...
- windows平台快速搭建Linux(CentOS)
VMware简介 VMware Workstation(简称 “虚拟机”)是一款功能强大的桌面虚拟计算机软件,用户可在Windows平台通过VMware软件同时运行不同的操作系统.这对IT开发人员而言 ...
- 分布式监控系统Zabbix-3.0.3-完整安装记录 - 添加shell脚本监控
对公司的jira访问状态进行监控,当访问状态返回值是200的时候,脚本执行结果为1:其他访问状态返回值,脚本执行结果是0.然后将该脚本放在zabbix进行监控,当非200状态时发出报警.jira访问状 ...
- Shell学习笔记二
一.调试脚本 调试功能是每一种编程语言都应该实现的重要特性之一,当出现一些始料未及的情况时,用它来生成脚本运行信息.调试信息可以帮你弄清楚是什么原因使得程序发生崩溃或行为异常.每位系统程序员都应该了解 ...
- 如何新增一个ssh-key文件
前言 由于在公司有一个sshkey 在用,用于绑定公司的git code 仓库.那么在家要连上git hub 仓库,就也需要一个 ssh key .为了避免公司信息外露,所以还是新增一个ssh key ...
- Rop框架学习笔记
1. 提供了开发服务平台的解决方案:比如应用认证.会话管理.安全控制.错误模型.版本管理.超时限制 2. 启动:RopServlet截获http请求 配置: <servlet> < ...
- Linux内核第三节 20135332武西垚
总结部分: Linux内核源代码: Arch 支持不同cpu的源代码:主要关注x86 Init 内核启动的相关代码:主要关注main.c,整个Linux内核启动代码start_kernel函数 K ...