微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

带有常规列表非堆化列表的 Heapop 和 Heappush

如何解决带有常规列表非堆化列表的 Heapop 和 Heappush

所以我看过很多帖子,其中用户在尝试从常规列表中进行 heappop 时会得到“不寻常/意外”的结果。 (ex: Unusual result from heappop?) 解决办法当然是先堆起来。

然而,对于这个 LeetCode solution heapq 方法用于常规列表,在使用这些方法之前没有进行堆化,但仍返回正确的预期结果。这是因为当您在常规列表上使用 heappop/heappush 时,它只是弹出/添加列表中的第一个元素?

解决方法

在示例中,他们在最初包含单个元素(源)的列表上使用 heappop,因此它满足堆属性。
在使用 heapifyheappop 等函数之前,不必在列表上使用 heappush。事实上,列表可能是空的,包含单个元素,或者是一个已经满足堆属性的列表。

示例:

>>> l = [1,3,2,5,4] # initial list that respects the heap property
>>> heappop(l)
1
>>> heappop(l)
2
>>> heappop(l)
3
>>> heappop(l)
4
>>> heappop(l)
5
,

heapq 模块没有定义任何新的数据类型来表示堆。堆只是一个列表(或者实际上是任何序列),它遵循 heap invariant:

堆是数组,其中 a[k] <= a[2*k+1]a[k] <= a[2*k+2] 用于所有 k,从 0 开始计算元素。

为了使列表不是堆,找到一个不变量为假的索引k是必要且充分的。

这对于空列表和单项列表是不可能的,因为所需的列表元素根本不存在,所以两者都是简单的堆。

对于包含 2 个或更多元素的列表,总是至少有一个条件可以为假,即 a[0] <= a[1]

heappushheappop 都被记录为“保持堆不变性”:如果每个函数的第一个参数在函数被调用之前是一个堆,那么在函数返回后它仍然是一个堆。 (如果第一个参数不是堆,它们的行为基本上是未定义的。)

以下是每个的定义:

def heappush(heap,item):
    """Push item onto heap,maintaining the heap invariant."""
    heap.append(item)
    _siftdown(heap,len(heap)-1)

私有函数 _siftdown 负责在附加 item 后恢复堆不变量。

def heappop(heap):
    """Pop the smallest item off the heap,maintaining the heap invariant."""
    lastelt = heap.pop()    # raises appropriate IndexError if heap is empty
    if heap:
        returnitem = heap[0]
        heap[0] = lastelt
        _siftup(heap,0)
        return returnitem
    return lastelt

私有函数 _siftup 负责在将 heap[0] 替换为 lastelt 后恢复堆不变量。


在您链接的代码中,pq 被初始化为一个单项列表,正如我们之前提到的,它已经是一个堆。由于 pq 仅在随后通过对 heappopheappush 的调用进行修改,因此在函数调用期间它仍然是一个堆。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。