Python:从堆中删除元素

时间:2012-04-15 14:00:35

标签: python heap

Python有heapq模块,它实现了堆数据结构,它支持一些基本操作(push,pop)。

如何从O(log n)中的堆中删除第i个元素?甚至可以用heapq还是我必须使用另一个模块?

请注意,文档底部有一个示例: http://docs.python.org/library/heapq.html 这提出了一种可能的方法 - 这不是我想要的。我想要删除元素,而不仅仅是标记为删除。

2 个答案:

答案 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)堆是一个列表。您可以按索引删除元素,就像任何其他列表一样,但是您需要重新堆积它,因为它将不再满足堆不变量。