携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天
算法
SPFA(Shortest Path Faster Algorithm)算法是单源最短路径的一种算法,通常被认为是 Bellman-ford 算法的队列优化,在代码形式上接近于宽度优先搜索 BFS,是一个在实践中非常高效的单源最短路算法。
①、算法流程
在 算法中,使用 表示从源点到顶点的最短路,额外用一个队列来保存即将进行拓展的顶点列表,并用 来标识顶点是不是在队列中。
1.初始队列中仅包含源点,且源点 的 。
2.取出队列头顶点 ,扫描从顶点 出发的每条边,设每条边的另一端为 ,边 权值为 ,若 ,则
-
将 修改为
-
若 不在队列中,则
-
将 入队
3.重复步骤 2 直到队列为空
最终 数组就是从源点出发到每个顶点的最短路距离。如果一个顶点从没有入队,则说明没有从源点到该顶点的路径。
的空间复杂度为。如果顶点的平均入队次数为 ,则 的时间复杂度为 O,对于较为随机的稀疏图,根据经验 一般不超过 4。
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1e3 + 9;
const int M = 1e4 + 9;
struct edge{
int v, w, fail;
edge(){}
edge(int _v, int _w, int _fail){
v = _v;
w = _w;
fail = _fail;
}
}e[M << 1];
int head[N], len;
void init(){
memset(head, -1, sizeof(head));
len = 0;
}
void add(int u, int v, int w){
e[len] = edge(v, w, head[u]);
head[u] = len++;
}
void add2(int u, int v, int w){
add(u, v, w);
add(v, u, w);
}
int n, m;
int dis[N];
bool vis[N];
void spfa(int u){
memset(vis, false, sizeof(vis));
vis[u] = true;
memset(dis, 0x3f, sizeof(dis));
dis[u] = 0;
queue<int> q;
q.push(u);
while(!q.empty()){
u = q.front();
q.pop();
vis[u] = false;
for(int j = head[u];~j;j = e[j].fail){
int v = e[j].v;
int w = e[j].w;
if(dis[v] > dis[u] + w){
dis[v] = dis[u] + w;
if(!vis[v]){
q.push(v);
vis[v] = true;
}
}
}
}
}
int main() {
init();
int u, v, w;
cin>>n>>m;
while(m--){
cin>>u>>v>>w;
add2(u, v, w);
}
spfa(1);
cout<<dis[n]<<endl;
return 0;
}
②、判断负环
不能处理有负权的图,而 可以处理任意不含负环(负环是指总边权和为负数的环)的图的最短路,并能判断图中是否存在负环
但是 可以用来判断负环,在进行 时,用一个数组 来标记每个顶点入队次数。如果一个顶点入队次数 大于顶点总数 n,则表示该图中包含负环。一般情况下, 判负环都只用在有向图上,因为在无向图上,一条负边权的边就是一个负环了
memset(in, 0, sizeof in);
in[u] = 1;
// 修改入队部分的操作
if(!vis[v]){
q.push(v);
vis[v] = true;
++in[v];
if(in[v] > n){
return true;
}
}