首页 > 编程语言 >Bellman-Ford\SPFA单源最短路算法

Bellman-Ford\SPFA单源最短路算法

时间:2024-12-31 21:52:30浏览次数:1  
标签:松弛 短路 单源 Bellman 负环 vis SPFA dis

Bellman-Ford单源最短路算法

不采用 SPFA 实现的Bellman-Ford算法

" 题目中的图没有特殊性质时,若 SPFA 是标算的一部分,题目不应当给出 Bellman–Ford 算法无法通过的数据范围 "


Bellman-Ford的原理如下

先枚举节点,在枚举边,每进行一轮循环,对图上所有的边都尝试进行一次松弛操作,当一次循环中没有成功的松弛操作时,退出循环

  • 判断负环的存在 (有\(n\) 个节点)

对于最短路存在的情况下,对这个节点进行一次松弛操作会使最短路的边数至少增加 \(1\) ,又因为图上的最短路的边数最多为 \(n-1\)

所以松弛操作最多只会执行 \(n-1\) 轮

如果第 \(n\) 轮时我们还能对一条边进行松弛操作,说明能够到达一个负环,在这个环上没有最短路定义,可以一直松弛下去

struct edge {
    int v, w; // 目标节点 v 和边权重 w
};

void init(void){
    memset(dis,0x3f,sizeof dis);
}

bool bellman_ford(int s){
    init();
    dis[s]=0;
    bool flag;
    for (int i=1;i<=n;i++){//进行 n 轮
        flag=0;
        for (int u=1;u<=n;u++){//遍历所有节点
            if (dis[u]==0x3f3f3f3f) continue;
            for (auto it=e[u].begin();it!=e[u].end();it++){//遍历与u连通的边
                if (dis[it->v]>dis[u]+it->w){//松弛操作
                    dis[it->v]=dis[u]+it->w;
                    flag=1;//标记这一轮可以松弛
                }
            }
        }
        if (!flag) break;//如果无法松弛,提前退出循环
    }
    return flag;//返回最后的标志,判断是否存在负环
}

其中,定义 flag 来判断是否能进行松弛操作,最后在第 \(n\) 轮时 flag 仍然为 \(1\) ,说明存在负环

if (dis[u]==0x3f3f3f3f) continue; 表示如果当前节点不能到达,那就跳过这个节点

  • 时间复杂度判断

对于每轮循环,内层的两个 for 都在干一件事情,那就是更新所有的边,所以一轮的复杂度就为 \(O(m)\),总的时间复杂度就为 \(O(nm)\)

优化方案——SPFA(Shortest Path Faster Algorithm)

朴素的算法中,我们会执行的不必要操作就是每轮都遍历所有的边

事实上,只有上一轮更新过的点才能引起这一轮的松弛操作,所以可以把上一轮更新的点记录下来,这一轮直接对这些点进行松弛操作

采用的是队列 queue 这一数据结构来维护更新的点

queue<int> q;

另外还需要cntvis 数组来记录当前节点最短路的边数以及是否在队中

bool vis[2010];
int cnt[2010];

SPFA:

bool spfa(int s) {
	init();
	dis[s] = 0;
	q.push(s);
	vis[s] = 1;
	while (q.size())
	{
		int t = q.front(); q.pop();
		vis[t] = 0;//取出队列
		for (auto it = e[t].begin(); it != e[t].end(); it++) {
			if (dis[it->v] > dis[t] + it->w) {
				dis[it->v] = dis[t] + it->w;
				cnt[it->v] = cnt[t] + 1;
				if (cnt[it->v] >= n) return 1;//判断负环
				if (!vis[it->v]) {//压入队列
					q.push(it->v);
					vis[it->v] = 1;
				}
			}
		}
	}
	return 0;
}

首先将起点 s 压入队列,vis[s] = 1 表明在队中

  • 循环直到队列为空,即不能松弛结束
  • 根据最短路的边数来判断负环

如果一个点的最短路边数大于等于了节点数 n ,说明一定存在一个负环导致某些边的经过次数大于 \(1\) ,及时退出

如果当前的节点能够进行松弛操作,并且当前还不在队列中,就把他压入队列


大多数情况下 SPFA 跑得很快,但其最坏情况下的时间复杂度和朴素算法一样为 \(O(nm)\)

谨防卡 SPFA ,非负权图就用 Dijkstra

标签:松弛,短路,单源,Bellman,负环,vis,SPFA,dis
From: https://www.cnblogs.com/dianman/p/18644813

相关文章

  • 代码随想录算法训练营第六十天|Bellman_ford队列优化法(SPFA)、bellman_ford之判断负
    前言打卡代码随想录算法训练营第49期第六十天(づ◕‿◕)づ首先十分推荐学算法的同学可以先了解一下代码随想录,可以在B站卡哥B站账号、代码随想录官方网站代码随想录了解,卡哥清晰易懂的算法教学让我直接果断关注,也十分有缘和第49期的训练营大家庭一起进步。Bellman_ford队......
  • 代码随想录算法训练营第五十九天|dijkstra(堆优化版)精讲、Bellman_ford
    前言打卡代码随想录算法训练营第49期第五十九天⚆_⚆(˘❥˘)(•̀⌓•)シ(人•͈ᴗ•͈)♡♡首先十分推荐学算法的同学可以先了解一下代码随想录,可以在B站卡哥B站账号、代码随想录官方网站代码随想录了解,卡哥清晰易懂的算法教学让我直接果断关注,也十分有缘和第49期的......
  • Dijkstra单源最短路堆优化算法
    Dijkstra单源最短路堆优化算法使用基于堆的优先队列,我们可以在进行松弛操作前对找边进行优化操作时间复杂度为\(O(m\logm)\),其中\(m\)为边的数量,优先队列找边的时间复杂度为\(O(\logm)\)优先队列默认为一个大根堆,即堆顶的元素的优先级最高,体现在某个变量的值上每次从队......
  • 数据结构与算法学习笔记----SPFA算法
    数据结构与算法学习笔记----SPFA算法@@author:明月清了个风@@firstpublishtime:2024.12.19SPFA算法这同样是一种单源最短路径算法,是队列优化的Bellman-ford算法,因此他能处理的情况和Bellman-ford算法一样,但是在一般情况下,时间复杂度更低,为......
  • Dijkstra单源最短路朴素算法(空间优化)
    Dijkstra单源最短路朴素算法(空间优化)基于使用邻接表存储连接边的方法,可以有效的降低空间复杂度在稀疏图(边的数量远小于顶点数量平方的图)中,邻接矩阵会大量占用无用的内存,导致Re,我们采用邻接表的办法,只存储存在的边,减少无关占用。相反,在稠密图(边的数量接近顶点数的平方的图)中,邻接......
  • 课程设计/单源最短路径问题的求解
    一、问题分析    问题描述:给定一个带权有向图G=(V,E),其中每条边的权是一个实数。另外,还给定V中的一个顶点,称为源。现在要计算从源到其他所有各顶点的最短路径长度。这里的长度就是指路上各边权之和,这个问题通常称为单源最短路径问题。该问题的经典求解方法为迪克斯特......
  • [模板] 单源最短路 Dijksra
    单源最短路:Dijksra单源最短路,时间复杂度\(O(nlog(n+m))\)不适用于有负权边的图#include<bits/stdc++.h>usingnamespacestd;usingll=longlong;structedge{intto,w;};vector<vector<edge>>adj;vector<int>dis;vector<bool>vis;intn,m,......
  • 迪克斯特拉算法:单源最短路径问题
    一、迪杰斯特拉算法的介绍迪杰斯特拉(Dijkstra)算法是一种用于计算加权图中单源最短路径的经典算法,由荷兰计算机科学家艾兹赫·迪杰斯特拉(EdsgerDijkstra)于1956年提出。迪杰斯特拉算法的核心思想是通过贪心策略,不断选择当前路径代价最小的节点,并逐步扩展搜索范围,直到找到从源节......
  • 【图论】单源最短路径 Dijkstra
    单源出发,最短路径,松弛。Dijkstra算法是所有最短路径算法中某种意义上最快的,只是代码略为难写。松弛Dijkstra算法的精髓,就是两个字:松弛。包括所有的最短路径算法,都依靠的是这个词。何为松弛?假设现有若干个点,点\(v\)到起点的最短路径很大,但是有另一个点\(u\),它的路径值较......
  • Bellman-ford算法
    有边数限制的最短路 #include<bits/stdc++.h>usingnamespacestd;constintN=510,M=10010,INF=0x3f3f3f3f;structEdge{inta,b,c;}edges[M];intn,m,k;intdist[N],last[N];//copy数组intbellman_ford(){memset(dist,0x3f,sizeofdist);dist[1......