Dijkstra的复杂性是否正确?

时间:2013-05-16 06:02:22

标签: c++ algorithm dijkstra

我对Dijkstra算法的运行时复杂性有疑问。 (参见CLRS vertion 3中的伪代码):

DIJKSTRA(G, w, s)
1 INITIALIZE-SINGLE-SOURCE(G, s)
2 S ← ∅ 
3 Q ← V[G]
4 while Q != ∅ 
5   do u ← EXTRACT-MIN(Q)
6   S ← S ∪ {u} 
7   for each vertex v ∈ Adj[u]
8     do RELAX(u, v,w)

我知道line3是O(V),line5总共是O(VlogV); line7总共为O(E),line8表示reduce_key(),因此每个Relax()操作都是logV。但是在relax()中,在d [v]> d [u] +权重并决定放松之后,我们不应该在调用reduce_key之前查找队列Q中v的位置(Q,pos,d [v ])用d [v]替换pos的关键字?注意这个查找本身需要花费O(V)。所以每个Relax()应该花费O(V),而不是O(logV),对吗?

关于空间复杂性的问题:为了比较队列Q中的顶点,我设计了一个带有距离的结构/类顶点作为一个成员,然后我实现了诸如operator<通过比较它们的距离来排序顶点。但似乎我必须定义一个重复的数组dist [],以便在Relax()中做dist [v] = dist [u] +权重。如果我没有定义重复数组,我必须在队列Q中查找v和u的位置,然后获取并检查它们的距离。是假设以这种方式工作?或者我的实施可能不好?

1 个答案:

答案 0 :(得分:4)

Dijkstra的算法(正如您所写的)没有运行时复杂性,除非您指定数据结构。你在某种程度上是正确的说“第7行”是O(E)操作的问题,但让我们来看看(幸运的是,Dijkstra“很容易”分析)。

  1. 初始化意味着“给所有顶点一个无限远的距离,除了距离为0的源。很简单,这可以在O(V)中完成。

  2. S套装的优点是什么?你使用它“只写”。

  3. 您将所有元素都放入队列中。这里是龙。什么是(优先级!)队列?具有操作添加的数据结构,可选地reduceKey(Dijkstra需要),删除(Dijkstra中不需要),extractMin。根据实现,这些操作具有某些运行时。例如,你可以构建一个只是一个(标记)集的哑PQ - 然后添加和减少一个键是恒定时间,但是为了提取最小值,你必须搜索。 Dijkstra中的规范解决方案是使用一个队列(如堆)来实现O(log n)中的所有相关操作。让我们分析这个案例,虽然从技术上讲,斐波那契堆会更好。不要自己实现队列。通过使用真正的PQ实现,您可以节省多少钱。

  4. 你经历了n次。

  5. 每次提取最小值,最小值为O(n log n)(在所有迭代中)。

  6. 套装S的优点是什么?

  7. 你最多经过一次每个顶点的边缘,也就是说每个边缘最多两次,所以你总是在O(E)次循环中做任何事情。

  8. 放松意味着检查您是否必须减少密钥并执行此操作。我们已经知道每个这样的操作都可以在队列中添加O(log V)(如果它是一个堆),我们必须做O(E)次,所以它是O(E log V),它占据了总运行时间

  9. 如果你服用Fibonacci-Heap,你可以下到O(VlogV + E),但这是学术性的。真正的实现调整堆。如果您想了解实施的性能,请分析PQ操作。但正如我所说,如果你不确切知道你在做什么,最好使用现有的实现。你在“调用reduceKey之前查找一个位置”的想法告诉我,在你想出一个实现有效地每次插入O(V)(通过每次调用一些reduceKey进行排序)或O(O)之前,你应该深入研究该主题。 V)每extractMin(通过查找最低要求)。