【问题标题】:built-in max heap API in PythonPython 中内置的最大堆 API
【发布时间】:2016-01-06 13:45:04
【问题描述】:

默认 heapq 是最小队列实现,想知道是否有最大队列选项?谢谢。

我尝试使用 _heapify_max 作为最大堆的解决方案,但是如何动态处理 push/pop 元素?似乎 _heapify_max 只能在初始化期间使用。

import heapq

def heapsort(iterable):
    h = []
    for value in iterable:
        heapq.heappush(h, value)
    return [heapq.heappop(h) for i in range(len(h))]

if __name__ == "__main__":

    print heapsort([1, 3, 5, 7, 9, 2, 4, 6, 8, 0])

编辑,尝试 _heapify_max 似乎不适用于动态推送/弹出元素。我试过两种方法输出一样,输出都是,[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]。

def heapsort(iterable):
    h = []
    for value in iterable:
        heapq.heappush(h, value)
    return [heapq.heappop(h) for i in range(len(h))]

def heapsort2(iterable):
    h = []
    heapq._heapify_max(h)
    for value in iterable:
        heapq.heappush(h, value)
    return [heapq.heappop(h) for i in range(len(h))]

if __name__ == "__main__":

    print heapsort([1, 3, 5, 7, 9, 2, 4, 6, 8, 0])
    print heapsort2([1, 3, 5, 7, 9, 2, 4, 6, 8, 0])

提前致谢, 林

【问题讨论】:

  • @LukasGraf,我不确定调用函数 heapify_max 是否好,因为我看到前缀“”,这似乎是一个内部函数?
  • @LukasGraf,第一个解决方案不适合我,因为我需要同时处理整数和字符串。 :)
  • 是的,这里的情况并不令人满意。尽管如此,question 本身几乎是完全重复的。但是,您可能会发现 this answer 很有帮助。
  • _heapify_max 会将您的输入转换为最大堆。但是,heappop 和 heappush 仍然是基于 min-heap 的。可以在这里查看heapq模块的源码:github.com/python/cpython/blob/master/Lib/heapq.py其实有一个_heappop_max函数可以导入,应该在max heap中使用。没有 _heappush_max 可用。但是你可以很容易地修改 heappush 函数来编写一个。或者在这里查看我的版本:github.com/he-zhe/heapq_max/blob/master/heapq_max/…

标签: python algorithm queue priority-queue


【解决方案1】:

在最新的 cpython 源代码中有一个 _heappop_max 函数,您可能会发现它很有用:

def _heappop_max(heap):
    """Maxheap version of a heappop."""
    lastelt = heap.pop()    # raises appropriate IndexError if heap is empty
    if heap:
        returnitem = heap[0]
        heap[0] = lastelt
        heapq._siftup_max(heap, 0)
        return returnitem
    return lastelt

如果您使用heapq._siftdown_max 更改heappush 逻辑,您应该会得到所需的输出:

def _heappush_max(heap, item):
    heap.append(item)
    heapq._siftdown_max(heap, 0, len(heap)-1)


def _heappop_max(heap):
    """Maxheap version of a heappop."""
    lastelt = heap.pop()  # raises appropriate IndexError if heap is empty
    if heap:
        returnitem = heap[0]
        heap[0] = lastelt
        heapq._siftup_max(heap, 0)
        return returnitem
    return lastelt


def heapsort2(iterable):
    h = []
    heapq._heapify_max(h)
    for value in iterable:
        _heappush_max(h, value)
    return [_heappop_max(h) for i in range(len(h))]

输出:

In [14]: heapsort2([1,3,6,2,7,9,0,4,5,8])
Out[14]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

In [15]: heapsort2([7, 8, 9, 6, 4, 2, 3, 5, 1, 0])
Out[15]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

In [16]: heapsort2([19,13,15,17,11,10,14,20,18])
Out[16]: [20, 19, 18, 17, 15, 14, 13, 11, 10]

In [17]: heapsort2(["foo","bar","foobar","baz"])
Out[17]: ['foobar', 'foo', 'baz', 'bar']

【讨论】:

    【解决方案2】:

    过去我只是简单地使用了sortedcontainers的SortedList,如:

    > a = SortedList()
    > a.add(3)
    > a.add(2)
    > a.add(1)
    > a.pop()
    3
    

    它不是堆,但速度很快,可以根据需要直接工作。

    如果你绝对需要它成为一个堆,你可以创建一个通用的否定类来保存你的项目。

    class Neg():
        def __init__(self, x):
            self.x = x
    
        def __cmp__(self, other):
            return -cmp(self.x, other.x)
    
    def maxheappush(heap, item):
        heapq.heappush(heap, Neg(item))
    
    def maxheappop(heap):
        return heapq.heappop(heap).x
    

    但这会占用更多内存。

    【讨论】:

    • 好收获。想知道您的解决方案是否适用于字符串,除了数字类型?
    • @LinMa 这就是包装器的用途。如果你知道你的数据是数字的,你可以通过使用 - 做得更好一点,就像在 def maxheappush(heap, item): heapq.heappush(heap, -item) 和 def maxheappop(heap): return -heapq.heappop(heap) 中一样。
    • @LinMa 是的,在Neg 实例上的a < b 之类的操作将调用__cmp__。如果你想看到它被调用,你可以添加一些打印语句。
    • @anveshtummala 有索引,ala a[-1]。
    • 很抱歉对旧线程进行了死灵处理,但针对上面的最后一条评论,我认为应该是a[0] 来查看最大项(即堆/树根部的项)。
    猜你喜欢
    • 1970-01-01
    • 2012-09-06
    • 2015-03-17
    • 2018-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 2012-08-07
    相关资源
    最近更新 更多