如何用斐波那契堆实现Prim算法?
我知道 Prim 算法 并且我知道它的实现,但我总是跳过我想要的部分现在就问。据记载,Prim 的算法实现 斐波那契堆 是 O(E + V log( V))
和我的问题是:
- 简单来说什么是斐波那契堆?
- 它是如何实施的?如何
- 使用 Fibonacci 堆实现 Prim 算法?
I know Prim's algorithm and I know its implementation but always I skip a part that I want to ask now. It was written that Prim's algorithm implementation with Fibonacci heap is O(E + V log(V))
and my question is:
- what is a Fibonacci heap in brief?
- How is it implemented? And
- How can you implement Prim's algorithm with a Fibonacci heap?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
斐波那契堆是一个相当复杂的优先级队列,它的所有操作都具有出色的摊余渐近行为 - 插入、查找最小值和减少键都在 O(1) 摊余时间内运行,而删除和提取最小值在摊余 O 时间内运行(lgn) 时间。如果您想要有关该主题的良好参考,我强烈建议您购买 CLRS 出版的《算法导论,第二版》并阅读其中的章节。它写得非常好并且非常说明性。 Fredman 和 Tarjan 关于斐波那契堆的原始论文< /a> 可以在线获取,您可能想查看一下。它很致密,但可以很好地处理材料。
如果你想看看斐波那契堆和 Prim 算法的实现,我必须为我自己的实现提供一个无耻的插件:
这两个实现中的注释应该很好地描述了它们的工作原理;如果有什么我可以澄清的,请告诉我!
A Fibonacci heap is a fairly complex priority queue that has excellent amoritzed asymptotic behavior on all its operations - insertion, find-min, and decrease-key all run in O(1) amortized time, while delete and extract-min run in amortized O(lg n) time. If you want a good reference on the subject, I strongly recommend picking up a copy of "Introduction to Algorithms, 2nd Edition" by CLRS and reading the chapter on it. It's remarkably well-written and very illustrative. The original paper on Fibonacci heaps by Fredman and Tarjan is available online, and you might want to check it out. It's dense, but gives a good treatment of the material.
If you'd like to see an implementation of Fibonacci heaps and Prim's algorithm, I have to give a shameless plug for my own implementations:
The comments in both of these implementations should provide a pretty good description of how they work; let me know if there's anything I can do to clarify!
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)
Prim's algorithm selects the edge with the lowest weight between the group of vertexes already selected and the rest of the vertexes.
So to implement Prim's algorithm, you need a minimum heap. Each time you select an edge you add the new vertex to the group of vertexes you've already chosen, and all its adjacent edges go into the heap.
Then you choose the edge with the minimum value again from the heap.
So the time complexities we get are:
Fibonacci:
Choosing minimum edge = O(time of removing minimum) = O(log(E)) = O(log(V))
Inserting edges to heap = O(time of inserting item to heap) = 1
Min heap:
Choosing minimum edge = O(time of removing minimum from heap) = O(log(E)) = O(log(V))
Inserting edges to heap = O(time of inserting item to heap) = O(log(E)) = O(log(V))
(Remember that E =~ V^2 ... so log(E) == log(V^2) == 2Log(V) = O(log(V))
So, in total you have E inserts, and V minimum choosings (It's a tree in the end).
So in Min heap you'll get:
O(Vlog(V) + Elog(V)) == O(Elog(V))
And in Fibonacci heap you'll get:
O(Vlog(V) + E)
几年前我使用斐波那契堆实现了 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 算法,那么使用斐波那契堆并没有真正的困难 - 只需使用堆的 insert 和 deletemin 方法像平常一样,并在释放通向顶点的边时使用 decreasekey 方法来更新顶点。
唯一困难的部分是实现实际的斐波那契堆。
我无法在这里向您提供所有实现细节(这将需要几页),但是当我这样做时,我严重依赖 算法简介(Cormen 等人)。如果您还没有它但对算法感兴趣,我强烈建议您获取它的副本!它与语言无关,它提供了有关所有标准算法及其证明的详细解释,并且肯定会提高您使用所有这些算法以及设计和证明新算法的知识和能力。 此 PDF(来自您链接的维基百科页面)提供了一些实现细节,但绝对不如算法简介那么清晰。
我有一份报告和一份演示文稿 我在这样做之后写道,解释了一些如何继续(对于 Dijkstra - 请参阅 ppt 末尾的斐波那契堆函数以伪代码表示),但都是法语...以及我的 代码< /a> 是 Caml(和法语),所以我不确定这是否有帮助!如果你能理解其中的一些内容,请宽容,我刚刚开始编程,所以当时我的编码技能非常差......
I implemented Dijkstra using Fibonacci heaps a few years ago, and the problem is pretty similar. Basically, the advantage of Fibonacci heaps is that it makes finding the minimum of a set a constant operation; so that's very appropriate for Prim and Dijkstra, where at each step you have to perform this operation.
Why it's good
The complexity of those algorithms using a binomial heap (which is the more "standard" way) is O(E * log V), because - roughly - you will try every edge (E), and for each of them you will either add the new vertex to your binomial heap (log V) or decrease its key (log V), and then have to find the minimum of your heap (another log V).
Instead, when you use a Fibonacci heap the cost of inserting a vertex or decreasing its key in your heap is constant so you only have a O(E) for that. BUT deleting a vertex is O(log V), so since in the end every vertex will be removed that adds a O(V * log V), for a total O(E + V * log V).
So if your graph is dense enough (eg E >> V), using a Fibonacci heap is better than a binomial heap.
How to
The idea is thus to use the Fibonacci heap to store all the vertices accessible from the subtree you already built, indexed by the weight of the smallest edge leading to it. If you understood the implementation or Prim's algorithm with using another data structure, there is no real difficulty in using a Fibonacci heap instead - just use the insert and deletemin methods of the heap as you would normally, and use the decreasekey method to update a vertex when you release an edge leading to it.
The only hard part is to implement the actual Fibonacci heap.
I can't give you all the implementation details here (that would take pages), but when I did mine I relied heavily on Introduction to algorithms (Cormen et al). If you don't have it yet but are interested in algorithms I highly recommend that you get a copy of it! It's language agnostic, and it provides detailed explanations about all the standards algorithms, as well as their proofs, and will definitely boost your knowledge and ability to use all of them, and design and prove new ones. This PDF (from the Wikipedia page you linked) provides some of the implementation details, but it's definitely not as clear as Introduction to algorithms.
I have a report and a presentation I wrote after doing that, that explain a bit how to proceed (for Dijkstra - see the end of the ppt for the Fibonacci heap functions in pseudo-code) but it's all in French... and my code is in Caml (and French) so I'm not sure if that helps!!! And if you can understand something of it please be indulgent, I was just starting programming so my coding skills were pretty poor at the time...