来自维基百科的伪代码:
function Dijkstra(Graph, source):
2 for each vertex v in Graph: // Initializations
3 dist[v] := infinity ; // Unknown distance function from source to v
4 previous[v] := undefined ; // Previous node in optimal path from source
5 end for ;
6 dist[source] := 0 ; // Distance from source to source
7 Q := the set of all nodes in Graph ; // All nodes in the graph are unoptimized - thus are in Q
8 while Q is not empty: // The main loop
9 u := vertex in Q with smallest distance in dist[] ; // Start node in first case
10 if dist[u] = infinity:
11 break ; // all remaining vertices are inaccessible from source
12 end if ;
13 remove u from Q ;
14 for each neighbor v of u: // where v has not yet been removed from Q.
15 alt := dist[u] + dist_between(u, v) ;
16 if alt < dist[v]: // Relax (u,v,a)
17 dist[v] := alt ;
18 previous[v] := u ;
19 decrease-key v in Q; // Reorder v in the Queue
20 end if ;
21 end for ;
22 end while ;
23 return dist[] ;
24 end Dijkstra.
现在,在第14行中,我们看到放宽仅适用于尚未从u
移除的Q
的邻居。但是,如果我们也将u
的{{1}}邻居从Q
移除,我认为算法 可以使用负权重。我没有发现任何与此声明相矛盾的实例。
那么为什么Dijkstra的算法不会这样改变呢?
答案 0 :(得分:12)
您当然可以使用负值使Dijkstra的算法工作,只需确保您不会遍历任何节点或边缘两次。在这里,通过 work ,我的意思是终止。然而,结果可能不是最佳的。
Dijkstra的算法有一种贪婪的感觉。想象一下下面的图表:
A --- 1 --- B
| |
3 -4
| |
C -- -1 --- D
如果我们想从A到B,最好的路径是A-C-D-B,但是Dijkstra的算法找到A-B。你不能让Dijkstra的算法预测未来,因为它是一种贪婪的算法。通过预测未来我的意思是,稍后知道路径的成本可能会降低。请注意,这意味着如果将修改应用于Dijkstra算法的版本,只要看到目标就会终止,则修改将无法正常工作。在您发布的版本中,您的修改有效,除了有更有效的方法来处理负边缘(请参阅注释)。
作为旁注,具有负值的无向图中的最短路径或具有负循环的有向图甚至没有意义!
答案 1 :(得分:6)
Dijkstra可以一次访问每个节点,因为当它选择要访问的新节点时,他会从根节点中选择具有最短路径的非访问节点。因此,他可以安全地假设没有更短的方式通过另一个非访问节点进入该节点(因为如果从A到B知道的最佳方式成本为2,从A到C的最佳方式成本为3,没有机会找到从A到B的更好的方法,例如A&gt; C&gt; B)。
如果你添加负权重,你会突然违背这个假设。
您当然可以使用建议的修改,但是您将失去仅访问每个节点一次的好处;因此,与Ford-Bellman等其他算法相比,它会失去性能的提升
答案 2 :(得分:4)
你基本上有两种选择。
您可以按照建议更改算法。如果你有没有负循环的有向图,那么这是一个正确的算法,但它可能非常慢(因为你将多次访问每个节点)。我认为有一种情况是这种算法具有指数时间复杂度。
您可以使用潜在客户来改变边缘成本。每个顶点具有一些势能h(v),并且边缘u-> v的权重将是w(u,v)+ h(u)-h(v)。请注意,这不会影响给定的两个顶点(s,t)之间的哪条路径是最短的,只是其成本由h(s) - h(t)改变。 但是你需要计算潜力。这里建议采用这种方法:http://en.wikipedia.org/wiki/Johnson's_algorithm
答案 3 :(得分:3)
不,不可能如上所述。除非您严格限制所提供的图表类型,否则该算法不会使用负权重 sense 。
假设一个图表包含节点A,B,C和权重AB = -1,BA = 0,BC = 1的边。
现在A和C之间不再存在最短路径,并且您可以通过在A和B之间来回再次来制作更短的路径。
当然,算法仍会运行,但它会给错误答案。
答案 4 :(得分:2)
是的,您的修改会在您未提及的两个假设下运作,但我猜是暗示的:
但是,您放弃了Dijkstra算法的一个重要特征:它具有良好的最坏情况渐近性能。现成的Dijkstra保证了良好的性能,因为它最多访问每个节点和每个边缘一次。包含您的更改后,已经删除的节点可以重新添加到优先级队列中,并且可能需要一次又一次地访问图形的大部分内容。在最坏的情况下,您必须执行与Bellman-Ford算法一样多的放松,但是您需要额外的优先级队列开销。这使得你的最坏情况表现比Bellman-Ford更差,因此对于具有负边缘的图表来说是首选。
这并不意味着您修改的Dijkstra不是很有用。它可以比Bellman-Ford表现得更好,如果你有很少的负边缘和/或如果这些负边缘通过昂贵的路径与图的其余部分分开。但要为最糟糕的表现做好准备。
答案 5 :(得分:1)
好吧,只是在Dijkstra的修改版本中放松已经访问过的节点是不够的(它会在包含负边缘的图形中给出错误的答案)。此外,您需要将任何宽松的边缘放入容器中(例如,优先级队列或只是队列)。因此,您可以将第19行中的代码修改为:
if v is in priority queue
then decrease-key(v)
else
add v into priority queue
在这种情况下,您的算法可以正常工作。此外,对于正加权图,Dijkstra的修改版本的效率不会降低。这很容易证明,因为这自然是一种贪婪的算法。
然而,对于包含负边的图,新算法在理论分析方面可能会变慢,但在实践中它会很好。
实际上,你可以看一下丁凡端在中国发表的一种名为SPFA(最短路径快速算法)的算法(1994)。很多OIer(奥林匹克的信息)知道这个算法有时可能会击败Dijkstra。