【问题标题】:How to implement Prim's algorithm with a Fibonacci heap?如何用斐波那契堆实现 Prim 算法?
【发布时间】:2011-01-28 06:30:15
【问题描述】:

我知道Prim's algorithm 并且我知道它的实现,但我总是跳过我现在想问的部分。有人写到,Prim 的算法实现与Fibonacci heapO(E + V log(V))我的问题是:

  • 简而言之,斐波那契堆是什么?
  • 它是如何实现的?并且
  • 如何使用斐波那契堆实现 Prim 算法?

【问题讨论】:

  • 你知道什么是二项式堆吗?解释起来有很大的不同。

标签: algorithm data-structures graph-algorithm prims-algorithm fibonacci-heap


【解决方案1】:

斐波那契堆是一个相当复杂的优先级队列,它的所有操作都具有出色的分摊渐近行为 - 插入、查找最小值和减少键都在 O(1) 分摊时间内运行,而删除和提取最小值运行在摊销的 O(lg n) 时间内。如果您想对该主题有很好的参考,我强烈建议您阅读 CLRS 的“算法简介,第 2 版”并阅读相关章节。它写得非常好,非常具有说明性。 The original paper on Fibonacci heaps by Fredman and Tarjan 可在线获取,您可能想查看一下。它很致密,但对材料的处理效果很好。

如果你想看斐波那契堆和 Prim 算法的实现,我必须为我自己的实现提供一个无耻的插件:

  1. My implementation of a Fibonacci heap.
  2. My implementation of Prim's algorithm using a Fibonacci heap.

这两种实现中的 cmets 应该很好地描述了它们的工作原理;如果有什么我可以澄清的,请告诉我!

【讨论】:

  • 谢谢。从我在 SO 的第一天到现在的最佳答案。我会写在我的个人资料上
  • 致那些投反对票的人——你能解释一下你的理由是什么以及我能做些什么来解决这个问题吗?
【解决方案2】:

Prim 的算法在已选择的顶点组和其余顶点之间选择权重最低的边。
所以要实现 Prim 的算法,你需要一个最小堆。每次选择一条边时,都会将新顶点添加到您已经选择的顶点组中,并且它的所有相邻边都进入堆。
然后再次从堆中选择最小值的边。

所以我们得到的时间复杂度是:
斐波那契:
选择最小边 = O(去除最小值的时间) = O(log(E)) = O(log(V))
向堆插入边 = O(向堆插入项的时间) = 1

最小堆:
选择最小边 = O(从堆中删除最小值的时间) = O(log(E)) = O(log(V))
向堆中插入边 = O(向堆中插入项的时间) = O(log(E)) = O(log(V))

(记住 E =~ V^2 ... 所以 log(E) == log(V^2) == 2Log(V) = O(log(V))

所以,你总共有 E 个插入和 V 个最小选择(最后是一棵树)。

所以在最小堆中你会得到:

O(Vlog(V) + Elog(V)) == O(Elog(V))

在斐波那契堆中你会得到:

O(Vlog(V) + E)

【讨论】:

  • 有一点计算错误...已修复
  • 次要备注:我认为“Min heap”应该是“Binary heap”,或者类似的东西。最小堆是一种抽象的数据结构;斐波那契堆可用于实现最小堆。
【解决方案3】:

几年前我使用斐波那契堆实现了 Dijkstra,问题非常相似。基本上,斐波那契堆的优势在于它使寻找集合的最小值成为一个常数操作。所以这对于 Prim 和 Dijkstra 来说非常合适,在每个步骤中您都必须执行此操作。

为什么好

使用二项式堆(这是更“标准”的方式)的那些算法的复杂性是 O(E * log V),因为 - 大致 - 您将尝试每条边 (E),并且对于每个边,您将新顶点添加到您的二项式堆 (log V) 或减少其键 (log V),然后必须找到堆的最小值(另一个 log V)。

相反,当您使用斐波那契堆时,在堆中插入顶点或减少其键的成本是恒定的,因此您只有 O(E)。但是删除一个顶点是 O(log V),所以最后每个顶点都会被删除,增加一个 O(V * log V),总共 O(E + V * log V)。

因此,如果您的图足够密集(例如 E >> V),则使用斐波那契堆比二项式堆更好。

如何

因此,这个想法是使用斐波那契堆来存储从您已经构建的子树中可访问的所有顶点,并按通向它的最小边的权重进行索引。如果您使用另一种数据结构来理解实现或 Prim 算法,那么使用 Fibonacci 堆并没有真正的困难 - 只需使用堆的 insertdeletemin 方法像往常一样,并使用 decreasekey 方法在释放通向它的边时更新顶点。

唯一困难的部分是实现实际的斐波那契堆。

我不能在这里给你所有的实现细节(这需要几页),但是当我这样做时,我非常依赖Introduction to algorithms (Cormen et al)。如果您还没有它但对算法感兴趣,我强烈建议您获取它的副本!它与语言无关,它提供了有关所有标准算法及其证明的详细解释,并且肯定会提高您使用所有这些算法以及设计和证明新算法的知识和能力。 This PDF(来自您链接的维基百科页面)提供了一些实现细节,但肯定不如算法简介那么清楚。

我有一个report 和一个presentation,我在这样做之后写了一个,解释了如何继续(对于 Dijkstra - 请参阅伪代码中的斐波那契堆函数的 ppt 结尾)但仅此而已法语...而我的code 是Caml(和法语),所以我不确定这是否有帮助!!!如果您能理解其中的一些内容,请放纵,我刚开始编程,所以当时我的编码技能很差...

【讨论】:

  • 实用说明:Prim 算法比 Dijkstra 算法更适合使用斐波那契堆。 Dijkstra 执行一个 extractMin 和 K reductionKey(或 Insert)操作的循环; Prim 使用 K extractMin 和 K inserts 的循环(K 是节点的平均度数)。在斐波那契堆上,连续的 extractMin 操作几乎是免费的,而在其他类型的堆上它们非常昂贵。
猜你喜欢
  • 2013-03-01
  • 2012-12-29
  • 2018-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-16
  • 2010-11-24
  • 2013-03-31
相关资源
最近更新 更多