Bellman-Ford算法的改进---SPFA算法
传送门:
SPFA
1.算法思想
Bellman-Ford算法时间复杂度比较高,在于Bellman-Ford需要递推n次,每次递推需要扫描所有的边,在递推n次的过程中,很多判断是多余的,所以考虑用队列优化,减少不必要的判断,这种算法称为SPFA(Shortest Path Faster Algorithm)
SPFA算法的大致流程就是用一个队列来进行维护,初始时将源点加入队列,每次从队列中取出一个顶点,并对它所有相邻的节点进行松弛,如果某个顶点松弛成功,则将其入队,重复这样的过程,直至队列为空为止。时间复杂度在O(Km)(通常K为2左右)一个顶点可以多次入队,但是如果有顶点入队次数大于n次,那就存在负环,此时应当返回存在负环信息
2.算法过程
在SPFA算法中同样可以用dist数组表示最短路长度,path数组保存路径,还需要设置cnt数组记录入队次数,vis数组记录当前是否在队列中
(1).取出队列头结点u,扫描从顶点u出发的每条边,设每条边的终点为v,边的权值为w(u, v)。如果dist[u] + w < dist[v],则将dist[v]修改成dist[u] + w<u, v>。修改path[v] = u,如果顶点v不在队列中,还需要将v加入队列并且入队次数加一。如果上述条件不成立就不做任何处理
(2).重复1直至队列为空或者某个顶点入队次数大于n
3.算法实现
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<sstream>
using namespace std;
typedef long long ll;
const int maxn = + ;
const int INF = << ;
int T, n, m, cases;
struct Edge
{
int u, v, w;
Edge(){}
Edge(int u, int v, int w):u(u), v(v), w(w){}
};
vector<Edge>edges;//把每一条边存下来
vector<int>Map[maxn];//G[i]这个vector存的是以i为起点的所有边在edges里面的下标
void init(int n)
{
for(int i = ; i <= n; i++)Map[i].clear();
edges.clear();
}
void addedge(int u, int v, int w)
{
edges.push_back(Edge(u, v, w));//注意无向图需要存两条边
m = edges.size();
Map[u].push_back(m - );
}
void Find(int u)//遍历以u为起点的所有边
{
for(int i = ; i < Map[u].size(); i++)
{
Edge&e = edges[Map[u][i]];
//使用e就可以遍历以u为起点的所有的边
}
}
int cnt[maxn];
bool vis[maxn];
int d[maxn], path[maxn];
bool SPFA(int u)
{
queue<int>q;
memset(vis, , sizeof(vis));//初始化
memset(cnt, , sizeof(cnt));
memset(path, -, sizeof(path));
for(int i = ; i < n; i++)d[i] = INF;
d[u] = ;
vis[u] = ;//标记进入队列
q.push(u);
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = ;//清除进入队列标记
for(int i = ; i < Map[u].size(); i++)
{
Edge& e = edges[Map[u][i]];
if(d[u] < INF && d[e.v] > d[u] + e.w)
{
d[e.v] = d[u] + e.w;
path[e.v] = Map[u][i];//path存的是边的下标,这样可以通过边找出之前的点以及每条路的路径,如果用邻接矩阵存储的话这里可以直接存节点u
if(!vis[e.v])
{
q.push(e.v);
vis[e.v] = ;
if(++cnt[e.v] > n)return true;//进队次数大于n,说明存在负环
}
}
}
}
for(int i = ; i < n; i++)
{
if(i == u)continue;
printf("从%d到%d距离是:%2d ", u, i, d[i]);
stack<int>q;//存的是边的编号
int x = i;//x就是路径上所有的点
while(path[x] != -)
{
q.push(x);
x = edges[path[x]].u;//x变成这条边的起点
}
cout<<u;
while(!q.empty())
{
cout<<"->"<<q.top();
q.pop();
}
cout<<endl;
}
return false;
}
int main()
{
int c;
cin >> n >> c;
int u, v, w;
for(int i = ; i < c; i++)
{
cin >> u >> v >> w;
addedge(u, v, w);
}
if(SPFA())cout<<"存在负环"<<endl;
else cout<<"不存在负环"<<endl;
return ;
}
Bellman-Ford算法的改进---SPFA算法的更多相关文章
- 算法笔记_071:SPFA算法简单介绍(Java)
目录 1 问题描述 2 解决方案 2.1 具体编码 1 问题描述 何为spfa(Shortest Path Faster Algorithm)算法? spfa算法功能:给定一个加权连通图,选取一个 ...
- 字符串匹配(BF算法和KMP算法及改进KMP算法)
#include <stdio.h> #include <string.h> #include <stdlib.h> #include<cstring> ...
- java实现SPFA算法
1 问题描述 何为spfa(Shortest Path Faster Algorithm)算法? spfa算法功能:给定一个加权连通图,选取一个顶点,称为起点,求取起点到其它所有顶点之间的最短距离,其 ...
- 最短路-SPFA算法&Floyd算法
SPFA算法 算法复杂度 SPFA 算法是 Bellman-Ford算法 的队列优化算法的别称,通常用于求含负权边的单源最短路径,以及判负权环. SPFA一般情况复杂度是O(m)最坏情况下复杂度和朴素 ...
- KMP算法的改进
KMP算法的改进 KMP算法已经在极大程度上提高了子符串的匹配效率,但是仍然有改进的余地. 1. 引入的情景 下面我们就其中的一种情况进行分析: 主串T为"aaaabcde-" 子 ...
- Bellman-Ford算法与SPFA算法详解
PS:如果您只需要Bellman-Ford/SPFA/判负环模板,请到相应的模板部分 上一篇中简单讲解了用于多源最短路的Floyd算法.本篇要介绍的则是用与单源最短路的Bellman-Ford算法和它 ...
- 使用spfa算法判断有没有负环
如果存在最短路径的边数大于等于点数,就有负环 给定一个n个点m条边的有向图,图中可能存在重边和自环, 边权可能为负数. 请你判断图中是否存在负权回路. 输入格式 第一行包含整数n和m. 接下来m行每行 ...
- 数据结构与算法--最短路径之Bellman算法、SPFA算法
数据结构与算法--最短路径之Bellman算法.SPFA算法 除了Floyd算法,另外一个使用广泛且可以处理负权边的是Bellman-Ford算法. Bellman-Ford算法 假设某个图有V个顶点 ...
- Bellman—Ford算法思想
---恢复内容开始--- Bellman—Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题.对于给定的带权(有向或无向)图G=(V,E),其源点为s,加权函数w是边集E的映射.对图G ...
随机推荐
- 排序算法Java实现(直接插入排序)
算法描述:对于给定的一个数组,初始时假设第一个记录自成一个有序序列,其余记录为无序序列.接着从第二个记录开始,按照记录的大小依次将当前处理的记录插入到其之前的有序序列中,直至最后一个记录插入到有序序列 ...
- 在linux下如何使用yum查看安装了哪些软件包
$yum list installed //列出所有已安装的软件包 yum针对软件包操作常用命令: 1.使用YUM查找软件包 命令:yum search 2.列出所有可安装的软件包 命令:yum li ...
- 设计模式 --> (11)桥接模式
桥接模式 将抽象部分与它的实现部分分离,使它们都可以独立地变化. 适用性: 1.当一个对象有多个变化因素的时候,考虑依赖于抽象的实现,而不是具体的实现.如上面例子中手机品牌有2种变化因素,一个是品牌, ...
- Laravel 模型事件入门
Laravel 模型事件允许你监听模型生命周期内的多个关键点,甚至可以在阻止一个模型的保存或者删除. Laravel 模型事件文档 概述了如何使用钩子将对应事件与相关的事件类型关联起来,但是本文的主旨 ...
- 听翁恺老师mooc笔记(8)--字符串2
字符串的赋值 字符串的输入与输出 对C语言的基础类型,比如int.double等类型,scanf.printf有专门的格式转换,而对字符串,scanf.printf使用%s格式字符进行输入与输出.当使 ...
- 关于C语言的第0次作业
1.你认为大学的学习生活.同学关系.师生关系应该是怎样的?请一个个展开描述. 我认为的大学学习生活是充实的,丰富多彩的,与高中快节奏.繁忙的生活有所不同.在上了大学我们都成熟了很多,懂得了包容与忍让, ...
- 数据结构——线性表——队列(queue)
队列也是一种特殊的线性表,它的特点是先入先出(FIFO,即first in first out).它的意思也很直观,想象一下排队买票,先排的人先买(插队是不对的,所以别去想).它也是很常用的数据结构, ...
- 软件工程第三次作业-结对作业NO.1
第一次结对作业 结对人员: 潘伟靖 170320077 张 松 170320079 方案分析 我们对所供的资料进行分析,如下: 从提供的资料可以看出,需要解决的问题以及满足的需求主要有两类目标用户,各 ...
- Flask 扩展 Mail
安装 pip install flask-mail from flask import Flask from flask_mail import Mail, Message app = Flask(_ ...
- jvm垃圾收集器总结jdk1.7
内存 ● 线程私有:程序计数器,虚拟机栈,本地方法栈 ● 线程共享: 方法区,堆 判断存活算法 ● 引用计数法:无法解决循环引用问题. ● 可达性分析算法: 从GCRoot作为起始点,向下搜索,经过的 ...