Python bao gồm mô-đun heapq cho các heap tối thiểu, nhưng tôi cần một heap tối đa. Tôi nên sử dụng gì để triển khai heap tối đa trong Python?
Python bao gồm mô-đun heapq cho các heap tối thiểu, nhưng tôi cần một heap tối đa. Tôi nên sử dụng gì để triển khai heap tối đa trong Python?
Câu trả lời:
Cách dễ nhất là đảo ngược giá trị của các khóa và sử dụng heapq. Ví dụ: biến 1000.0 thành -1000.0 và 5.0 thành -5.0.
heapq
không cung cấp một đảo ngược.
heapq
, và không có sự thay thế tốt.
int
/ float
, bạn có thể đảo ngược thứ tự bằng cách gói chúng trong một lớp với __lt__
toán tử đảo ngược .
Bạn có thể dùng
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!!
Nếu sau đó bạn muốn bật các yếu tố, hãy sử dụng:
heapq.heappop(minheap) # pop from minheap
heapq._heappop_max(maxheap) # pop from maxheap
_heapify_max
, _heappushpop_max
, _siftdown_max
, và _siftup_max
.
Giải pháp là phủ nhận các giá trị của bạn khi bạn lưu trữ chúng trong heap hoặc đảo ngược so sánh đối tượng của bạn như vậy:
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)
Ví dụ về một heap tối đa:
maxh = []
heapq.heappush(maxh, MaxHeapObj(x))
x = maxh[0].val # fetch max value
x = heapq.heappop(maxh).val # pop max value
Nhưng bạn phải nhớ bọc và mở khóa các giá trị của mình, điều này đòi hỏi phải biết bạn đang xử lý một đống tối thiểu hay tối đa.
Thêm các lớp cho MinHeap
và MaxHeap
các đối tượng có thể đơn giản hóa mã của bạn:
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
Ví dụ sử dụng:
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"
list
tham số tùy chọn vào __init__ trong trường hợp đó tôi gọi heapq.heapify
và cũng đã thêm một heapreplace
phương thức.
Nhân các giá trị với -1
Có bạn đi. Tất cả các số cao nhất hiện nay là thấp nhất và ngược lại.
Chỉ cần nhớ rằng khi bạn bật một phần tử để nhân nó với -1 để lấy lại giá trị ban đầu.
Tôi đã triển khai một phiên bản heapq tối đa và gửi nó cho PyPI. (Thay đổi rất nhỏ mã CPython của mô-đun heapq.)
https://pypi.python.org/pypi/heapq_max/
https://github.com/he-zhe/heapq_max
Cài đặt
pip install heapq_max
Sử dụng
tl; dr: giống như mô đun heapq ngoại trừ thêm '_max' cho tất cả các chức năng.
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
Nếu bạn đang chèn các khóa có thể so sánh nhưng không giống int, bạn có khả năng ghi đè các toán tử so sánh trên chúng (tức là <= trở thành> và> trở thành <=). Mặt khác, bạn có thể ghi đè heapq._siftup trong mô-đun heapq (cuối cùng tất cả chỉ là mã Python).
# If available, use C implementation
) thực hiện chính xác những gì nhận xét mô tả.
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]
Mở rộng lớp int và ghi đè __lt__ là một trong những cách.
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()
Tôi đã tạo một trình bao bọc heap để đảo ngược các giá trị để tạo một heap tối đa, cũng như một lớp bao bọc cho một heap nhỏ để làm cho thư viện giống như OOP hơn. Đây là ý chính. Có ba lớp; Heap (lớp trừu tượng), HeapMin và HeapMax.
Phương pháp:
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
Trong trường hợp nếu bạn muốn lấy phần tử K lớn nhất bằng heap tối đa, bạn có thể thực hiện mẹo sau:
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]
nlargest
tại đây -> github.com/python/cpython/blob/ trên
Theo câu trả lời xuất sắc của Isaac Turner , tôi muốn đưa ra một ví dụ dựa trên K Điểm gần nhất với Nguồn gốc bằng cách sử dụng heap tối đa.
from math import sqrt
import heapq
class MaxHeapObj(object):
def __init__(self, val):
self.val = val.distance
self.coordinates = val.coordinates
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)
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 peek(self):
return heapq.nsmallest(1, self.h)[0].val
def __getitem__(self, i):
return self.h[i].val
class Point():
def __init__(self, x, y):
self.distance = round(sqrt(x**2 + y**2), 3)
self.coordinates = (x, y)
def find_k_closest(points, k):
res = [Point(x, y) for (x, y) in points]
maxh = MaxHeap()
for i in range(k):
maxh.heappush(res[i])
for p in res[k:]:
if p.distance < maxh.peek():
maxh.heappop()
maxh.heappush(p)
res = [str(x.coordinates) for x in maxh.h]
print(f"{k} closest points from origin : {', '.join(res)}")
points = [(10, 8), (-2, 4), (0, -2), (-1, 0), (3, 5), (-2, 3), (3, 2), (0, 1)]
find_k_closest(points, 3)
Để giải thích chi tiết về https://stackoverflow.com/a/59311063/1328979 , đây là một triển khai Python 3 được ghi chép đầy đủ, chú thích và thử nghiệm cho trường hợp chung.
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
Đây là một MaxHeap
thực hiện đơn giản dựa trên heapq
. Mặc dù nó chỉ hoạt động với các giá trị số.
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)
Sử dụng:
max_heap = MaxHeap()
max_heap.push(3)
max_heap.push(5)
max_heap.push(1)
print(max_heap.top()) # 5