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

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

244359 次浏览

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

如果您插入的键是可比较的,但不是int型的,您可能会重写它们上的比较操作符(即<=变成>,>变成<=)。否则,您可以重写heapq。heapq模块中的_siftup(最后都是Python代码)。

你可以使用

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

我创建了一个堆包装器,它将值颠倒以创建max-heap,还为min-heap创建了一个包装器类,以使库更像oop。在这里是要点。有三个班级;Heap(抽象类),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

我实现了一个最大堆版本的heapq,并将它提交给PyPI。(对heapq模块CPython代码的改动很小。)

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

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

安装

pip install heapq_max

使用

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

解决方案是当你在堆中存储你的值时对其求反,或者像这样反转你的对象比较:

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)

max-heap的例子:

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"

最简单最理想的解决方案

将这些值乘以-1

好了。所有最高的数字现在都是最低的,反之亦然。

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

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

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]

扩展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()

如果你想用max heap得到最大的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]

为了详细说明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

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

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

我还需要使用max-heap,而且我处理的是整数,所以我只是包装了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()调用。

最简单的方法

?

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

输出如下所示:

[-20, -1, -10]
heapq模块具有实现maxheap所需的一切。 它只做max-heap的堆推功能。 我已在下面示范如何克服这一点。

在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函数。首先写"进口heapq"在编辑器中,然后右键单击“heapq”并选择转到定义。

最好的方法:

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

在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())

输出:

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

< p > heapify之后 [50,30,20,8,9,10,0,7,5, -1, -2]

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

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

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))

试试这个

我已经创建了一个名为heap_class的包,它实现了最大堆,并将各种堆函数包装到一个列表兼容的环境中。

>>> from heap_class import Heap
>>> h = Heap([3, 1, 9, 20], max=True)
>>> h.pop()
20
>>> h.peek()  # same as h[0]
9
>>> h.push(17)  # or h.append(17)
>>> h[0]  # same as h.peek()
17
>>> h[1]  # inefficient, but works
9

从最大堆中获得最小堆。

>>> y = reversed(h)
>>> y.peek()
1
>>> y  # repr is inefficient, but correct
Heap([1, 3, 9, 17], max=False)
>>> 9 in y
True
>>> y.raw()  # underlying heap structure
[1, 3, 17, 9]
正如其他人所提到的,在max堆中处理字符串和复杂对象在heapq中是相当困难的,因为不同 否定的形式。heap_class实现简单:

>>> h = Heap(('aa', 4), ('aa', 5), ('zz', 2), ('zz', 1), max=True)
>>> h.pop()
('zz', 2)

支持自定义键,并与后续的推/追加和弹出一起工作:

>>> vals = [('Adam', 'Smith'), ('Zeta', 'Jones')]
>>> h = Heap(vals, key=lambda name: name[1])
>>> h.peek()  # Jones comes before Smith
('Zeta', 'Jones')
>>> h.push(('Aaron', 'Allen'))
>>> h.peek()
('Aaron', 'Allen')

(实现是建立在heapq函数上的,所以它都是用C语言或C语言包装的,除了Python中max heap上的heappush和heapreplace)