ChatGPT解决这个技术问题 Extra ChatGPT

我在 Python 中使用什么来实现最大堆?

Python 包含用于最小堆的 heapq 模块,但我需要一个最大堆。我应该在 Python 中使用什么来实现最大堆?


D
Daniel Stutzbach

最简单的方法是反转键的值并使用 heapq。例如,将 1000.0 变为 -1000.0,将 5.0 变为 -5.0。


这也是标准解决方案。
呃;总杂乱无章。我很惊讶 heapq 没有提供相反的内容。
哇。我惊讶这不是由 heapq 提供的,而且没有好的替代方案。
@gatoatigrado:如果您有一些不容易映射到 int/float 的东西,您可以通过使用反向 __lt__ 运算符将它们包装在一个类中来反转顺序。
@Aerovistae 同样的建议适用:反转值(即切换符号),无论开始是正面还是负面。
o
oerpli

您可以使用

import heapq
listForTree = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]    
heapq.heapify(listForTree)             # for a min heap
heapq._heapify_max(listForTree)        # for a maxheap!!

如果您想弹出元素,请使用:

heapq.heappop(minheap)      # pop from minheap
heapq._heappop_max(maxheap) # pop from maxheap

看起来最大堆有一些未记录的函数:_heapify_max_heappushpop_max_siftdown_max_siftup_max
哇。我很惊讶 heapq 中有这样一个内置的解决方案。但是在官方文档中根本没有提到它是完全不合理的!哇!
任何 pop/push 函数都会破坏最大堆结构,因此这种方法是不可行的。
不要使用它。正如 LinMa 和 Siddhartha 所注意到的,push/pop 打破了顺序。
以下划线开头的方法是私有的,可以在不事先通知的情况下删除。不要使用它们。
I
Isaac Turner

解决方案是在将值存储在堆中时取反,或者像这样反转对象比较:

import heapq

class MaxHeapObj(object):
  def __init__(self, val): self.val = val
  def __lt__(self, other): return self.val > other.val
  def __eq__(self, other): return self.val == other.val
  def __str__(self): return str(self.val)

最大堆示例:

maxh = []
heapq.heappush(maxh, MaxHeapObj(x))
x = maxh[0].val  # fetch max value
x = heapq.heappop(maxh).val  # pop max value

但是你必须记住包装和解包你的值,这需要知道你是在处理最小堆还是最大堆。

MinHeap、MaxHeap 类

MinHeapMaxHeap 对象添加类可以简化您的代码:

class MinHeap(object):
  def __init__(self): self.h = []
  def heappush(self, x): heapq.heappush(self.h, x)
  def heappop(self): return heapq.heappop(self.h)
  def __getitem__(self, i): return self.h[i]
  def __len__(self): return len(self.h)

class MaxHeap(MinHeap):
  def heappush(self, x): heapq.heappush(self.h, MaxHeapObj(x))
  def heappop(self): return heapq.heappop(self.h).val
  def __getitem__(self, i): return self.h[i].val

示例用法:

minh = MinHeap()
maxh = MaxHeap()
# add some values
minh.heappush(12)
maxh.heappush(12)
minh.heappush(4)
maxh.heappush(4)
# fetch "top" values
print(minh[0], maxh[0])  # "4 12"
# fetch and remove "top" values
print(minh.heappop(), maxh.heappop())  # "4 12"

好的。我采用了这个并向 __init__ 添加了一个可选的 list 参数,在这种情况下我调用 heapq.heapify 并添加了一个 heapreplace 方法。
很惊讶没有人发现这个错字:MaxHeapInt --> MaxHeapObj。否则,确实是一个非常干净的解决方案。
有趣的是,范辰宝对这个问题的回答非常相似:stackoverflow.com/questions/8875706/…
需要这条线吗? def __eq__(self, other): 返回 self.val == other.val。我认为没有它也可以工作。
@ChirazBenAbdelkader Fanchen Bao 的链接答案是使用带有自定义键对象的元组作为第一个元素,而不是使用自定义对象来包装元素,因此略有不同。 tuple 方法允许传递一个很酷的 lambda。
S
Sebastian Nielsen

最简单理想的解决方案

将值乘以 -1

你去吧。现在所有最高的数字都是最低的,反之亦然。

请记住,当您弹出一个元素以将其与 -1 相乘以再次获得原始值时。


很好,但大多数解决方案都支持类/其他类型,并且不会更改实际数据。悬而未决的问题是,将值乘以 -1 是否不会改变它们(非常精确的浮点数)。
@亚历克斯巴拉诺夫斯基。没错,但维护者的回应是:bugs.python.org/issue27295
维护者有权不实现某些功能,但这一个 IMO 实际上是有用的。
对于某些编码回合,这可能是一个很好的解决方案。否则在应用程序中更改数据听起来并不那么好。
T
Than Win Hline

最简单的方法是将每个元素转换为负数,它将解决您的问题。

import heapq
heap = []
heapq.heappush(heap, 1*(-1))
heapq.heappush(heap, 10*(-1))
heapq.heappush(heap, 20*(-1))
print(heap)

输出将如下所示:

[-20, -1, -10]

Z
Zhe He

我实现了 heapq 的最大堆版本并将其提交给 PyPI。 (heapq 模块 CPython 代码的微小变化。)

https://pypi.python.org/pypi/heapq_max/

https://github.com/he-zhe/heapq_max

安装

pip install heapq_max

用法

tl;dr:与 heapq 模块相同,除了向所有函数添加“_max”。

heap_max = []                           # creates an empty heap
heappush_max(heap_max, item)            # pushes a new item on the heap
item = heappop_max(heap_max)            # pops the largest item from the heap
item = heap_max[0]                      # largest item on the heap without popping it
heapify_max(x)                          # transforms list into a heap, in-place, in linear time
item = heapreplace_max(heap_max, item)  # pops and returns largest item, and
                                    # adds new item; the heap size is unchanged

Y
Yuchen

这是一个基于 heapq 的简单 MaxHeap 实现。虽然它只适用于数值。

import heapq
from typing import List


class MaxHeap:
    def __init__(self):
        self.data = []

    def top(self):
        return -self.data[0]

    def push(self, val):
        heapq.heappush(self.data, -val)

    def pop(self):
        return -heapq.heappop(self.data)

用法:

max_heap = MaxHeap()
max_heap.push(3)
max_heap.push(5)
max_heap.push(1)
print(max_heap.top())  # 5

又好又简单!
最容易理解的代码,无需解释。
V
Vikas Prasad

我还需要使用最大堆,并且我正在处理整数,所以我只是将我需要的两个方法从 heap 包装起来,如下所示:

import heapq


def heappush(heap, item):
    return heapq.heappush(heap, -item)


def heappop(heap):
    return -heapq.heappop(heap)

然后我分别用 heappush()heappop() 替换了我的 heapq.heappush()heapq.heappop() 调用。


r
rlotun

如果您要插入可比较但不类似于 int 的键,您可能会覆盖它们上的比较运算符(即 <= 变为 > 并且 > 变为 <=)。否则,您可以覆盖 heapq 模块中的 heapq._siftup (最后都是 Python 代码)。


“这只是 Python 代码”:这取决于您的 Python 版本和安装。例如,我安装的 heapq.py 在第 309 行 (# If available, use C implementation) 之后有一些代码完全符合注释的描述。
G
Gaurav

扩展 int 类并覆盖 __lt__ 是其中一种方法。

import queue
class MyInt(int):
    def __lt__(self, other):
        return self > other

def main():
    q = queue.PriorityQueue()
    q.put(MyInt(10))
    q.put(MyInt(5))
    q.put(MyInt(1))
    while not q.empty():
        print (q.get())


if __name__ == "__main__":
    main()

这是可能的,但我觉得它会减慢很多速度并使用大量额外的内存。 MyInt 也不能真正在堆结构之外使用。但是感谢您输入示例,这很有趣。
哈!在我发表评论后的一天,我遇到了需要将自定义对象放入堆中并需要最大堆的情况。实际上,我重新搜索了这篇文章并找到了您的答案,并以此为基础解决了我的问题。 (自定义对象是一个点,具有 x,y 坐标和 lt 覆盖比较与中心的距离)。感谢您发布此信息,我赞成!
i
illuminato

最好的办法:

from heapq import *
h = [5, 7, 9, 1, 3]
h_neg = [-i for i in h]
heapify(h_neg)            # heapify
heappush(h_neg, -2)       # push
print(-heappop(h_neg))    # pop
# 9

j
jasonleonhard

允许您选择任意数量的最大或最小项目

import heapq
heap = [23, 7, -4, 18, 23, 42, 37, 2, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
heapq.heapify(heap)
print(heapq.nlargest(3, heap))  # [42, 42, 37]
print(heapq.nsmallest(3, heap)) # [-4, -4, 2]

一个解释将是有序的。
我的回答比问题长。你想补充什么解释?
这给出了正确的结果,但实际上并没有使用堆来提高效率。该文档指定 nlargest 和 nsmallest 每次对列表进行排序。
n
noɥʇʎԀʎzɐɹƆ

我创建了一个堆包装器,它反转值以创建一个最大堆,以及一个用于最小堆的包装器类,以使库更像 OOP。 Here 是要点。分为三类;堆(抽象类)、HeapMin 和 HeapMax。

方法:

isempty() -> bool; obvious
getroot() -> int; returns min/max
push() -> None; equivalent to heapq.heappush
pop() -> int; equivalent to heapq.heappop
view_min()/view_max() -> int; alias for getroot()
pushpop() -> int; equivalent to heapq.pushpop

M
Marc Carré

为了详细说明 https://stackoverflow.com/a/59311063/1328979,这里有一个针对一般情况的完整记录、注释和测试的 Python 3 实现。

from __future__ import annotations  # To allow "MinHeap.push -> MinHeap:"
from typing import Generic, List, Optional, TypeVar
from heapq import heapify, heappop, heappush, heapreplace


T = TypeVar('T')


class MinHeap(Generic[T]):
    '''
    MinHeap provides a nicer API around heapq's functionality.
    As it is a minimum heap, the first element of the heap is always the
    smallest.
    >>> h = MinHeap([3, 1, 4, 2])
    >>> h[0]
    1
    >>> h.peek()
    1
    >>> h.push(5)  # N.B.: the array isn't always fully sorted.
    [1, 2, 4, 3, 5]
    >>> h.pop()
    1
    >>> h.pop()
    2
    >>> h.pop()
    3
    >>> h.push(3).push(2)
    [2, 3, 4, 5]
    >>> h.replace(1)
    2
    >>> h
    [1, 3, 4, 5]
    '''
    def __init__(self, array: Optional[List[T]] = None):
        if array is None:
            array = []
        heapify(array)
        self.h = array
    def push(self, x: T) -> MinHeap:
        heappush(self.h, x)
        return self  # To allow chaining operations.
    def peek(self) -> T:
        return self.h[0]
    def pop(self) -> T:
        return heappop(self.h)
    def replace(self, x: T) -> T:
        return heapreplace(self.h, x)
    def __getitem__(self, i) -> T:
        return self.h[i]
    def __len__(self) -> int:
        return len(self.h)
    def __str__(self) -> str:
        return str(self.h)
    def __repr__(self) -> str:
        return str(self.h)


class Reverse(Generic[T]):
    '''
    Wrap around the provided object, reversing the comparison operators.
    >>> 1 < 2
    True
    >>> Reverse(1) < Reverse(2)
    False
    >>> Reverse(2) < Reverse(1)
    True
    >>> Reverse(1) <= Reverse(2)
    False
    >>> Reverse(2) <= Reverse(1)
    True
    >>> Reverse(2) <= Reverse(2)
    True
    >>> Reverse(1) == Reverse(1)
    True
    >>> Reverse(2) > Reverse(1)
    False
    >>> Reverse(1) > Reverse(2)
    True
    >>> Reverse(2) >= Reverse(1)
    False
    >>> Reverse(1) >= Reverse(2)
    True
    >>> Reverse(1)
    1
    '''
    def __init__(self, x: T) -> None:
        self.x = x
    def __lt__(self, other: Reverse) -> bool:
        return other.x.__lt__(self.x)
    def __le__(self, other: Reverse) -> bool:
        return other.x.__le__(self.x)
    def __eq__(self, other) -> bool:
        return self.x == other.x
    def __ne__(self, other: Reverse) -> bool:
        return other.x.__ne__(self.x)
    def __ge__(self, other: Reverse) -> bool:
        return other.x.__ge__(self.x)
    def __gt__(self, other: Reverse) -> bool:
        return other.x.__gt__(self.x)
    def __str__(self):
        return str(self.x)
    def __repr__(self):
        return str(self.x)


class MaxHeap(MinHeap):
    '''
    MaxHeap provides an implement of a maximum-heap, as heapq does not provide
    it. As it is a maximum heap, the first element of the heap is always the
    largest. It achieves this by wrapping around elements with Reverse,
    which reverses the comparison operations used by heapq.
    >>> h = MaxHeap([3, 1, 4, 2])
    >>> h[0]
    4
    >>> h.peek()
    4
    >>> h.push(5)  # N.B.: the array isn't always fully sorted.
    [5, 4, 3, 1, 2]
    >>> h.pop()
    5
    >>> h.pop()
    4
    >>> h.pop()
    3
    >>> h.pop()
    2
    >>> h.push(3).push(2).push(4)
    [4, 3, 2, 1]
    >>> h.replace(1)
    4
    >>> h
    [3, 1, 2, 1]
    '''
    def __init__(self, array: Optional[List[T]] = None):
        if array is not None:
            array = [Reverse(x) for x in array]  # Wrap with Reverse.
        super().__init__(array)
    def push(self, x: T) -> MaxHeap:
        super().push(Reverse(x))
        return self
    def peek(self) -> T:
        return super().peek().x
    def pop(self) -> T:
        return super().pop().x
    def replace(self, x: T) -> T:
        return super().replace(Reverse(x)).x


if __name__ == '__main__':
    import doctest
    doctest.testmod()

https://gist.github.com/marccarre/577a55850998da02af3d4b7b98152cf4


R
Ritav Das

heapq 模块拥有实现 maxheap 所需的一切。它只执行 max-heap 的 heappush 功能。我在下面演示了如何克服下面的问题⬇

在 heapq 模块中添加这个函数:

def _heappush_max(heap, item):
    """Push item onto heap, maintaining the heap invariant."""
    heap.append(item)
    _siftdown_max(heap, 0, len(heap)-1)

最后添加:

try:
    from _heapq import _heappush_max
except ImportError:
    pass

瞧!完成。

PS-去heapq函数。首先在您的编辑器中写入“import heapq”,然后右键单击“heapq”并选择转到定义。


R
RowanX

如果您想使用最大堆获得最大的 K 元素,可以执行以下技巧:

nums= [3,2,1,5,6,4]
k = 2  #k being the kth largest element you want to get
heapq.heapify(nums) 
temp = heapq.nlargest(k, nums)
return temp[-1]

不幸的是,时间复杂度为 O(MlogM),其中 M = len(nums),这违背了 heapq 的目的。在此处查看 nlargest 的实现和评论 -> github.com/python/cpython/blob/…
感谢您提供信息丰富的评论,将确保检查所附链接。
S
Sar ibra

在python中有构建堆,但如果有人想像我一样自己构建它,我只想分享这个。我是python的新手,不要判断我是否犯了错误。算法正在运行,但关于效率我不知道

class Heap :

    def __init__(self):
        self.heap = []
        self.size = 0


    def add(self, heap):
        self.heap = heap
        self.size = len(self.heap)

    def heappush(self, value):
        self.heap.append(value)
        self.size += 1


    def heapify(self, heap ,index=0):

        mid = int(self.size /2)
        """
            if you want to travel great value from bottom to the top you need to repeat swaping by the hight of the tree
            I  don't how how can i get the  height of the tree that's why i use sezi/2
            you can find height by this formula
            2^(x) = size+1  why 2^x because tree is growing exponentially 
            xln(2) = ln(size+1)
            x = ln(size+1)/ln(2)
        """

        for i in range(mid):
            self.createTee(heap ,index)

        return heap

    def createTee(self,  heap ,shiftindex):

        """
        """
        """

            this pos reffer to the index of the parent only parent with children
                    (1)
                (2)      (3)           here the size of list is 7/2 = 3
            (4)   (5)  (6)  (7)        the number of parent is 3 but we use {2,1,0} in while loop
                                       that why a put pos -1

        """
        pos = int(self.size /2 ) -1
        """
            this if you wanna sort this heap list we should swap max value in the root of the tree with the last
            value in the list and if you wanna repeat this until sort all list you will need to prevent the func from
            change what we already sorted I should decrease the size of the list that will heapify on it

        """

        newsize = self.size - shiftindex
        while pos >= 0 :
            left_child = pos * 2 + 1
            right_child = pos * 2 + 2
            # this mean that left child is exist
            if left_child < newsize:
                if right_child < newsize:
                    # if the right child exit we wanna check if left child > rightchild
                    # if right child doesn't exist we can check that we will get error out of range
                    if heap[pos] < heap[left_child] and heap[left_child]  > heap[right_child] :
                        heap[left_child] , heap[pos] = heap[pos], heap[left_child]
                # here if the righ child doesn't exist
                else:
                    if heap[pos] < heap[left_child] :
                        heap[left_child] , heap[pos] = heap[pos], heap[left_child]
            # if the right child exist
            if right_child < newsize :
                if heap[pos] < heap[right_child] :
                    heap[right_child], heap[pos] = heap[pos], heap[right_child]
            pos -= 1

        return heap

    def sort(self ):
        k = 1
        for i in range(self.size -1 ,0 ,-1):
            """
            because this is max heap we swap root with last element in the list

            """
            self.heap [0] , self.heap[i] = self.heap[i], self.heap[0]
            self.heapify(self.heap ,k)
            k+=1

        return self.heap


h = Heap()
h.add([5,7,0,8,9,10,20,30,50,-1] )
h.heappush(-2)
print(" before heapify ")
print(h.heap)
print(" after heapify ")
print(h.heapify(h.heap,0))
print(" after sort ")
print(h.sort())

输出 :

在 heapify [5, 7, 0, 8, 9, 10, 20, 30, 50, -1, -2] 之前

堆后 [50, 30, 20, 8, 9, 10, 0, 7, 5, -1, -2]

排序后 [-2, -1, 0, 5, 7, 8, 9, 10, 20, 30, 50]

我希望你能理解我的代码。如果有什么你不明白的发表评论我会尽力帮助


s
spider_Malaya
arr = [3,4,5,1,2,3,0,7,8,90,67,31,2,5,567]
# max-heap sort will lead the array to assending order
def maxheap(arr,p):
    
    for i in range(len(arr)-p):
        if i > 0:
            child = i
            parent = (i+1)//2 - 1
            
            while arr[child]> arr[parent] and child !=0:
                arr[child], arr[parent] = arr[parent], arr[child]
                child = parent
                parent = (parent+1)//2 -1
                
    
def heapsort(arr):
    for i in range(len(arr)):
        maxheap(arr,i)
        arr[0], arr[len(arr)-i-1]=arr[len(arr)-i-1],arr[0]
        
    return arr
        

print(heapsort(arr))

尝试这个