使用heapq降序

时间:2017-06-28 02:10:06

标签: python python-2.7 python-3.x heap

我正在使用Python的 heapq 模块按升序和降序获取数据。

对于升序,我使用的是min heap,它运行良好如下:

>>> from heapq import heapify, heappop
>>> heap = [9, 3, 1, 5, 6, 2, 7]
>>> heapify(heap)
>>> heappop(heap)
1
>>> heappop(heap)
2
>>> heappop(heap)
3

对于降序,我尝试了不同的方法,但所有方法都有一些缺点:

  1. 使用负值作为优先级来获得反向排序。我必须使用单独的列表来使数据可重用。如果原始列表很大,那么列出副本的代价很高。

    >>> from heapq import heapify, heappop
    >>> heap = [9, 3, 1, 5, 6, 2, 7]
    >>> heap_neg = [-x for x in heap]
    >>> heapify(heap_neg)
    >>> -heappop(heap_neg)
    9
    >>> -heappop(heap_neg)
    7
    >>> -heappop(heap_neg)
    6
    
  2. 使用负值的元组作为优先级,这也浪费空间。我不想将整数列表存储为元组列表。

    >>> from heapq import heapify, heappop
    >>> heap = [(-9, 9), (-3, 3), (-1, 1), (-5, 5), (-6, 6), (-2,2), (-7,7)]
    >>> heapify(heap)
    >>> heappop(heap)[1]
    9
    >>> heappop(heap)[1]
    7
    >>> heappop(heap)[1]
    6
    
  3. 缺少使用密钥在heapify中排序。类似的东西:

    >>> from heapq import heapify, heappop
    >>> heap = [9, 3, 1, 5, 6, 2, 7]
    >>> heapify(heap, key=lambda x:-x) # This doesn't work as heapify don't have key parameter
    
  4. 如果我使用heapq._heapify_max(堆),我必须在每个元素弹出后_heapify_max。像:

    >>> from heapq import _heapify_max, heappop
    >>> heap = [9, 3, 1, 5, 6, 2, 7]
    >>> _heapify_max(heap)
    >>> heappop(heap)
    9
    >>> heappop(heap)  # popping without _heapify_max gives wrong result
    1
    >>> _heapify_max(heap)
    >>> heappop(heap) # popping after _heapify_max gives correct result
    7
    
  5. 有没有什么方法可以获得降序顺序,类似于升序顺序? :)

3 个答案:

答案 0 :(得分:2)

正如我们在评论中所讨论的那样,当你从一个空堆开始并随时添加值时,你在使用否定值将最小堆翻转到最大堆时复制数据的担忧并不重要。由于这是查找值流的运行中位数时的用例,因此在添加值时否定值应该可以正常工作。

这是一个运行中值生成器,我写的只是为了仔细检查它是否按照我预期的方式工作:

def running_median(iterable):
    left_q = [] # heap of smaller-than-median elements, stored negated
    right_q = [] # heap of larger-than-median elements

    for value in iterable:
        if len(left_q) == len(right_q): # push to left_q when they're equal size
            if len(right_q) > 0 and value > right_q[0]:
                value = heapq.heapreplace(right_q, value)
            heapq.heappush(left_q, -value)
        else: # push to right_q only when it's (strictly) smaller
            if value < -left_q[0]:
                value = -heapq.heapreplace(left_q, -value)
            heapq.heappush(right_q, value)

        # len(left_q) is always >= len(right_q) so we never yield right_q[0]
        if len(left_q) > len(right_q):
            yield -left_q[0]
        else:
            yield (-left_q[0] + right_q[0]) / 2

left_q堆存储小于或等于中值的值。当推送时,每个值都被否定,因此使用正常的最小堆操作使其像最大堆一样工作。我们只需要记住重新否定我们从中获取的任何价值,以回到原来的标志。

答案 1 :(得分:1)

我认为你正在寻找一个排序链表,在这种情况下,我修改了一个我发现here的人,所以它会按升序插入(我添加了pop函数,因为某些原因它不在代码,但我认为你可能需要它):

add

正如您所看到的,只是将&gt; =更改为&lt; =,它完美地完成了工作

答案 2 :(得分:0)

有一些专用方法(在python 3.8上测试)

import heapq


if __name__ == '__main__':
    a = [1, 3, 2, 5]

    heapq._heapify_max(a)

    for item in range(0, len(a)):
        print(heapq._heappop_max(a)

结果是

sorted heap  5
sorted heap  3
sorted heap  2
sorted heap  1

但是对于某些人来说,使用私有方法可能看起来不够正确。因此,我们可以通过将对象放置在修改后的包装器中来更改顺序

class DescOrder:
    def __init__(self, entity):
        self.entity = entity

    def __lt__(self, o):
        return self.entity.__gt__(o.entity)

    def __repr__(self):
        return str(self.entity)

def check_sorting(a, b):
    new_heap = []

    for element in a:
        heapq.heappush(new_heap, DescOrder(element))

    for index in range(0, len(b)):
        assert heapq.heappop(new_heap).entity == b[index]


if __name__ == '__main__':
    check_sorting([5, 1, -1, 3, 2], [5, 3, 2, 1, -1])
    check_sorting([5, 2, -1, 3, 1], [5, 3, 2, 1, -1])
    check_sorting([-1, 2, 5, 3, 1], [5, 3, 2, 1, -1])