Python有heapq
模块,它实现了堆数据结构,它支持一些基本操作(push,pop)。
如何从O(log n)中的堆中删除第i个元素?甚至可以用heapq
还是我必须使用另一个模块?
请注意,文档底部有一个示例: http://docs.python.org/library/heapq.html 这提出了一种可能的方法 - 这不是我想要的。我想要删除元素,而不仅仅是标记为删除。
答案 0 :(得分:39)
您可以非常轻松地从堆中删除第i个元素:
h[i] = h[-1]
h.pop()
heapq.heapify(h)
只需用最后一个元素替换要删除的元素,然后删除最后一个元素,然后重新堆积堆。这是O(n),如果你想要你可以在O(log(n))中做同样的事情,但是你需要调用几个内部的heapify函数,或者更好,因为larsmans指出只是复制源代码将heapq.py中的_siftup / _siftdown转换为您自己的代码:
h[i] = h[-1]
h.pop()
if i < len(h):
heapq._siftup(h, i)
heapq._siftdown(h, 0, i)
请注意,在每种情况下,您都不能只执行h[i] = h.pop()
,因为i
引用最后一个元素时会失败。如果您删除最后一个元素的特殊情况,那么您可以组合覆盖和弹出。
请注意,根据您的堆的典型大小,您可能会发现仅调用heapify
而理论上效率较低可能比重新使用_siftup
/ _siftdown
更快:一点点内省将揭示heapify
可能在C中实现,但内部函数的C实现未公开。如果性能对您很重要,那么请考虑对典型数据进行一些时序测试,看看哪个是最好的。除非你有很大的堆积,否则O可能不是最重要的因素。
修改:有人尝试编辑此答案,删除对_siftdown
的调用,并发表评论:
不需要_siftdown。新h [i]保证是老h [i]的孩子中最小的,这仍然比老h [i]的父母大 (新h [i]的父母)。 _siftdown将是一个无操作。我必须编辑 没有足够的代表来添加评论。
他们在此评论中遗漏的是h[-1]
可能根本不是h[i]
的孩子。在h[i]
处插入的新值可能来自堆的完全不同的分支,因此可能需要在任一方向上进行筛选。
还有评论询问为什么不使用sort()
来恢复堆:调用_siftup
和_siftdown
都是O(log n)操作,调用heapify是O(n) 。调用sort()
是一个O(n log n)操作。调用sort很可能足够快,但对于大堆来说,这是一个不必要的开销。
已编辑以避免@Seth Bruder指出的问题。当i
引用结束元素时,_siftup()
调用将失败,但在这种情况下,从堆末尾弹出一个元素不会破坏堆不变量。
答案 1 :(得分:11)
(a)考虑为什么你不想懒惰删除。在很多情况下,它是正确的解决方案。
(b)堆是一个列表。您可以按索引删除元素,就像任何其他列表一样,但是您需要重新堆积它,因为它将不再满足堆不变量。