Dijkstra?spfa?SPstra?

Dijkstra、SPFA及SPstra算法解析

带负权的无负环最短路问题

对于一张有负边权的图,普通 Dijkstra 就不能用了,比如:
在这里插入图片描述
正常的 Dijkstra 扩散的节点依次为 1 , 3 , 2 , 4 1,3,2,4 1,3,2,4
这时候可以发现,当点 2 2 2 扩散的时候,原本达到点 3 3 3 的路径长度是 1 1 1,路径 1 ⟶ 2 ⟶ 3 1\longrightarrow2\longrightarrow3 123 的权值之和为 − 2 -2 2,明显更短,这个时候点 1 1 1 到点 3 3 3 的路径长度就会被更新为 − 2 -2 2,但是,点 3 3 3 在点 2 2 2 之前已经被扩散过,不会在进行第二次扩散。此时,点 1 1 1 到点 4 4 4 的路径长度依旧是 1 + 2 = 3 1+2=3 1+2=3

这里说明了 Dijkstra 的一个缺陷,贪心思路会考虑不到后续的负数。

那么,我们可以对 Dijkstra 做一个改动,解决现在遇到的问题。
上述的问题发生的根源就是 v i s vis vis 数组,在当前点找到更优值的时 v i s vis vis 数组不会改变,就导致了上图中点 4 4 4 没有被更新,所以我们在找到更优值时就再给当前点一个机会。

if(dis[y]>dis[x]+z){
	vis[y]=0;
	dis[y]=dis[x]+z;
}

这样可以很好地解决上图中出现的问题,但是判断负环还是需要 SPFA
那么,我们将上述的改动版 Dijkstra 命名为SPstra
在这里插入图片描述

带负环的最短路问题

这个时候就可以考虑用到 SPFA。
SPFA 与 Dijkstra 的不同点:

入队条件

Dijkstra 的遍历概念是:扩散
而 SPFA 的遍历概念是:筛选
SPFA 的标记表示的是“是否在队列中”,也就是说有没有看过这个元素值更新后对相连点的影响。
如果没有在队列中,并且还找到了当前点更优的方案,那么当前点就该入队上班了。(可以借助文章最顶上的图来理解)。

出队标记

出队后,元素就不在队列中了,标记取消。

负环判断

负环是什么?
负环就是一个有向图中的环,并且对这个环遍历一圈得到的权值总和为负,那么这个时候就可以一直绕着这个环转圈圈,使权值总和越来越小。
我们假设:一张有 n n n 个点的有向图中有这样一个点 x x x,这个点十分有魅力,图中其余的 n − 1 n-1 n1 个点都有一条边连向这个点。

再极端一点,这其余的 n − 1 n-1 n1 个点按照被遍历到的顺序依次对点 x x x 有更优贡献。
那么,这个点就会被入队 n − 1 n-1 n1 次,这个时候还没有出现负环。
但是,如果这个点再被入队一次,入队次数达到了 n n n 次,就一定有一个点(暂且称为点 y y y)连接点 x x x 的这条路径被遍历了两次,这个时候就有了负环,因为点 x x x 在被点 y y y 连接的情况下还有一条负数边权和的路径连接着点 y y y

SPFA 可否使用优先队列优化

其实使用优先队列优化的 SPFA 就是前面的 “SPstra” 加上一个负环判断。

对于这个问题,答案是:能,但是很极端。

SPFA 优先队列优化的适用场景

对于正边权居多的图,可以使用优先队列优化,这样可以使效率接近 Dijkstra 的 O ( N log ⁡ N ) O(N\log N) O(NlogN)

普通 SPFA 的适用场景

对于负边权居多的图,其实普通队列和优先队列的遍历次数都是差不多的,只是顺序变了,而且优先队列还多了一个 O ( log ⁡ N ) O(\log N) O(logN),效率还不如普通队列。

总结

考试或者比赛时,题目的数据我们无从得知。
但我们知道的是,造数据的人们都是阴险狡诈忠恳善良的,我们可以猜疑相信他们。

### Dijkstra算法SPFA算法的区别 #### 1. 基本概念 Dijkstra算法是一种经典的单源最短路径算法,适用于无负权边的加权图。其核心思想是利用贪心策略逐步扩展已知距离最小的节点,并不断更新相邻节点的距离[^3]。 相比之下,SPFA(Shortest Path Faster Algorithm)是对Bellman-Ford算法的一种改进,同样可以用于求解单源最短路径问题。它的特点是能够高效处理含有负权边的图结构,但在某些情况下可能退化为O(VE)的时间复杂度[^5]。 --- #### 2. 负权边支持能力 Dijkstra算法无法处理带有负权边的图,因为在该算法中一旦某个节点被标记为“访问过”,就认为从起始点到此节点的距离已经是最优值。然而,在存在负权边的情况下,这种假设可能导致错误的结果[^3]。 相反,SPFA算法由于继承自Bellman-Ford算法,天然具备处理负权边的能力,只要不存在负环即可正常工作。 --- #### 3. 时间复杂度 当采用朴素实现方式时,Dijkstra算法的时间复杂度通常为 \( O(V^2) \),其中 V 表示顶点数;如果引入优先队列优化,则可降低至 \( O((V+E)\log V) \)[^4]。 对于SPFA而言,虽然理论上平均时间复杂度接近于线性级别即 \( O(E) \),但实际上取决于输入数据特性以及具体实现细节,极端情形下仍有可能达到 \( O(VE) \)[^5]。 以下是两种算法的核心伪代码对比: ```python # Dijkstra with Priority Queue Optimization import heapq def dijkstra(graph, start): dist = {node: float('inf') for node in graph} dist[start] = 0 pq = [(0, start)] # Min-Heap while pq: current_dist, u = heapq.heappop(pq) if current_dist > dist[u]: continue for v, weight in graph[u].items(): distance = current_dist + weight if distance < dist[v]: dist[v] = distance heapq.heappush(pq, (distance, v)) return dist # SPFA Implementation from collections import deque def spfa(graph, start): dist = {node: float('inf') for node in graph} dist[start] = 0 queue = deque([start]) in_queue = set(queue) while queue: u = queue.popleft() in_queue.remove(u) for v, weight in graph[u].items(): new_distance = dist[u] + weight if new_distance < dist[v]: dist[v] = new_distance if v not in in_queue: queue.append(v) in_queue.add(v) return dist ``` --- #### 4. 实现难度与适用场景 尽管两者的最终目标一致,但由于设计哲学不同,它们各自适合的应用领域有所区分。例如,在稀疏图环境下经过适当调整后的Dijkstra表现往往优于SPFA;而对于稠密或者包含少量负权重边的情形来说,后者可能是更好的选择[^1]。 此外值得注意的一点在于实际编码过程中还需要考虑到诸如边界条件判断等诸多因素的影响,这些都会进一步影响程序性能及稳定性[^4]。 --- ### 结论 综上所述,无论是Dijkstra还是SPFA都有各自的优点和局限之处,合理选用需依据具体情况而定。一般建议在确认网络不含任何负权边的前提下优先尝试效率更高的Dijkstra方法;反之则应转投灵活性更强却也可能带来更高计算成本的SPFA方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值