[英]Dijkstra's algorithm implementation tracing in python
我正在尝试使用优先级队列跟踪 Dijkstra 算法的 python 实现,但我无法遵循,因为我是 python 的新手
这是实现
def dijkstra(edges, f, t):
g = defaultdict(set)
for l,r,c in edges:
g[l].add((c,r))
g[r].add((c, l))
q, seen, = [(0,f,())], set(),
while q:
(weight, v1, path) = heappop(q)
if v1 not in seen:
seen.add(v1)
path += (v1,)
if v1 == t:
return weight, path
for k, v2 in g.get(v1, ()):
if v2 not in seen:
heappush(q, (weight+ k, v2, path))
return float("inf")
g = defaultdict(set)
而不是g = defaultdict(list)
和 used.add() 而不是.append()解释代码的每一行发生了什么的注释对我理解它真的很有帮助。
至于你的问题:
首先为什么它使用
g = defaultdict(set)
而不是g = defaultdict(list)
并使用.add()
而不是.append()
它与list
一样有效。 当然,要使用的方法( add
或append
)遵循此选择。 我能看到的唯一优点是使用set
可以避免两次添加相同的边缘。 一般来说,一个图在相同的两个顶点之间可以有多个边,它们甚至可以具有相同的权重:当这种情况发生时,没有理由单独考虑这些重复的边,并且该set
将确保忽略重复的边。
我知道在 Dijkstra 算法开始时,您需要将所有节点的所有权重设置为无穷大,但我在这里看不到。
有不同的方法来实现算法。 实际上,您可以在一开始就将所有顶点添加到优先级队列中,其中除了源顶点之外的所有顶点都以无穷大的权重开始。 但是从队列中排除那些“无穷大”顶点会更有效一些:这样队列大小会更小,并且添加到队列中的第一个顶点会稍微快一些。 因此,任何不在队列中的顶点实际上都是一个仍然具有无穷大权重的顶点。
节点在哪条线上决定它要经过的路径,就像在哪条线上做出向左或向右的决定一样。 简而言之,在代码中节点之间的加权线是在哪里制作的。
代码中没有可见的决定。 在找到目标节点之前,所有路径都是潜在的赢家。 在此之前,所有已构建的部分路径都在堆上,而堆的特性决定了哪条路径将是下一条将扩展到相邻节点的路径。 然后那些较长的路径(具有更多顶点)将再次被扔进堆中,堆的魔力将再次发挥作用。 因此,如果您寻找“决定”,则只有在堆内做出决定:它告诉我们哪条路径是堆中存在的权重最小的路径。 因此主循环可能在一条路径上工作一点(以扩展它),但在下一次迭代中它可能在完全不同的路径上工作。 就这样继续下去,直到突然发现它已经到达了目标顶点。 仅在那一刻,仍然是堆上的候选路径的所有其他路径都将被忽略。
如果您想更多地了解heappop
和heappush
的隐藏魔法,请阅读有关该主题的Wikipedia 文章。
虽然算法是正确的,但它不是一个有效的实现。 以下语句为要复制的路径提供案例,并且该路径可能有多达n 个元素,因此它在一次执行时具有O(n)的最坏情况时间复杂度,从而使算法的最坏情况时间复杂度为O( n²logn) :
path += (v1,)
为了避免这种情况,通常不跟踪整个路径,而只存储对我们形成的前一个节点的反向引用。 然后当我们到达目标节点的时候,我们可以沿着这些反向引用走回去并且只构建一次路径。 由于存储反向引用需要固定时间,因此这种改进将使算法的时间复杂度为O(nlogn) 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.