SPFA(Shortest Path Faster Algorithm) 算法是 Bellman-Ford算法 的队列优化算法的别称,通常用于求含负权边的单源最短路径,以及判负权环。SPFA 最坏情况下复杂度和朴素 Bellman-Ford 相同,为 O(VE)。
算法优点:
1.时间复杂度比普通的Dijkstra和Ford低。
2.能够计算有负权的图,即:路径减小时,会进行判断。
算法思想:
我们用数组记录每个结点的最短路径估计值,用邻接表来存储图G。
我们采取的方法是动态逼近法:
1.设立一个先进先出的队列用来保存待优化的结点。
2.优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行判断,如果v点的最短路径估计值有所上升或下降,且v点不在当前的队列中,就将v点放入队尾。
3.这样不断从队列中取出结点来进行松弛操作,直至队列空为止
期望的时间复杂度O(ke), 其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。
实现方法:
1、存入图。可以使用链式前向星或者vocter。
2、开一个队列,先将开始的节点放入。
3、每次从队列中取出一个节点X,遍历与X相通的Y节点,查询比对 Y的长度 和 X的长度+ 边(X,Y)的长度 如果X的长度+ 边(X,Y) > Y的长度,说明需要更新操作。
-
存入最短路。
-
由于改变了原有的长度,所以需要往后更新,与这个节点相连的最短路。(即:判断下是否在队列,在就不用重复,不在就加入队列,等待更新)。
-
在这期间可以记录这个节点的进队次数,判断是否存在负环。
4、直到队空。
判断有无负环:如果某个点进入队列的次数超过N次则存在负环
参考:http://keyblog.cn/article-21.html
【题目描述】
有 N 个网络节点,标记为 1 到 N。
给定一个列表 times,表示信号经过有向边的传递时间。 times[i] = (u, v, w),其中 u 是源节点,v 是目标节点, w 是一个信号从源节点传递到目标节点的时间。
现在,我们向当前的节点 K 发送了一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1。
注意:
- N 的范围在 [1, 100] 之间。
- K 的范围在 [1, N] 之间。
- times 的长度在 [1, 6000] 之间。
- 所有的边 times[i] = (u, v, w) 都有 1 <= u, v <= N 且 0 <= w <= 100。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/network-delay-time/
def networkDelayTime(self, times, N, K):
def spfa():
from collections import deque
from collections import defaultdict
edge_weight = {}
next_edges = defaultdict(list)
for u, v, w in times:
edge_weight[(u, v)] = w
next_edges[u].append(v)
# 第一种实现方法
# stack = deque()
# stack.append(K)
# path = [float('inf') for i in range(N + 1)]
# path[K] = 0
# while stack:
# cur = stack.popleft()
# if not next_edges.get(cur):
# continue
# for next in next_edges.get(cur):
# if path[cur] + edge_weight[(cur, next)] < path[next]:
# path[next] = path[cur] + edge_weight[(cur, next)]
# if next not in stack:
# stack.append(next)
# 第二种实现方法
stack = [K]
path = [float('inf')]*(N + 1)
path[K] = 0
while stack:
new_stack = []
for cur in stack:
if not next_edges.get(cur):
continue
for next in next_edges.get(cur):
if path[cur] + edge_weight[(cur, next)] < path[next]:
path[next] = path[cur] + edge_weight[(cur, next)]
if next not in new_stack:
new_stack.append(next)
stack = new_stack
return -1 if any(x == float('inf') for x in path[1:]) else max(path[1:])
return spfa()