Tôi sử dụng gì để triển khai heap tối đa trong Python?


Câu trả lời:


244

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.


38
Đó cũng là giải pháp chuẩn.
Andrew McGregor

44
xấu xí; tổng lượng bùn. Tôi ngạc nhiên heapqkhông cung cấp một đảo ngược.
shabbychef

40
Ồ Tôi ngạc nhiên rằng điều này không được cung cấp bởi heapq, và không có sự thay thế tốt.
ire_and_curses

23
@gatoatigrado: Nếu bạn có thứ gì đó không dễ ánh xạ tới 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 .
Daniel Stutzbach

5
Lời khuyên tương tự @Aerovistae được áp dụng: đảo ngược các giá trị (nghĩa là chuyển đổi dấu hiệu) bất kể bắt đầu bằng tích cực hay tiêu cực.
Dennis

235

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

34
Hình như có một số chức năng không có giấy tờ cho đống max: _heapify_max, _heappushpop_max, _siftdown_max, và _siftup_max.
ziyuang

127
Ồ Tôi ngạc nhiên rằng có một ví dụ tích hợp giải pháp trong heapq. Nhưng sau đó, hoàn toàn không hợp lý khi nó thậm chí KHÔNG được đề cập một chút nào trong tài liệu chính thức! WTF!
RayLuo

27
Bất kỳ chức năng pop / đẩy nào phá vỡ cấu trúc heap tối đa, vì vậy phương pháp này không khả thi.
Siddhartha

22
ĐỪNG SỬ DỤNG NÓ. Như LinMa và Siddhartha nhận thấy, đẩy / pop phá vỡ trật tự.
Alex Fedulov

13
Các phương thức bắt đầu bằng dấu gạch dưới là riêng tư và có thể được gỡ bỏ mà không cần thông báo trước . Đừng sử dụng chúng.
dùng4815162342

66

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.

Các lớp MinHeap, MaxHeap

Thêm các lớp cho MinHeapMaxHeapcá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"

Đẹp. Tôi đã lấy cái này và thêm một listtham số tùy chọn vào __init__ trong trường hợp đó tôi gọi heapq.heapifyvà cũng đã thêm một heapreplacephương thức.
Booboo

1
Ngạc nhiên vì không ai bắt gặp lỗi đánh máy này: MaxHeapInt -> MaxHeapObj. Nếu không, một giải pháp rất sạch thực sự.
Chiraz BenAbdelkader

Đã sửa lỗi @ChirazBenAbdelkader, cảm ơn bạn.
Isaac Turner

39

Giải pháp dễ dàng và lý tưởng nhất

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.


Tuyệt vời, nhưng hầu hết giải pháp đều hỗ trợ các lớp / loại khác và sẽ không thay đổi dữ liệu thực tế. Câu hỏi mở là nếu nhân giá trị với -1 sẽ không thay đổi chúng (float cực kỳ chính xác).
Alex Baranowski

1
@AlexBaranowski. Điều đó đúng, nhưng đó là phản hồi từ người bảo trì: bug.python.org/su27295
Flair

Những người bảo trì tốt có quyền không thực hiện một số chức năng, nhưng IMO này thực sự hữu ích.
Alex Baranowski

7

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

4

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


9
Tất cả chỉ là mã Python. Nó phụ thuộc vào phiên bản và cài đặt Python của bạn. Ví dụ: heapq.py đã cài đặt của tôi có một số mã sau dòng 309 ( # If available, use C implementation) thực hiện chính xác những gì nhận xét mô tả.
tzot

3

Cho phép bạn chọn một lượng tùy ý các mặt hàng lớn nhất hoặc nhỏ nhấ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]

3
Một lời giải thích sẽ theo thứ tự.
Peter Mortensen

Tiêu đề của tôi là lời giải thích của tôi
jasonleonhard

1
Câu trả lời của tôi dài hơn câu hỏi. Bạn muốn thêm lời giải thích nào?
jasonleonhard


2
Điều này cho kết quả chính xác nhưng thực tế không sử dụng một đống để làm cho nó hiệu quả. Tài liệu chỉ định rằng sắp xếp danh sách lớn nhất và nhanh nhất mỗi lần.
RossFovenant

3

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

Điều đó là có thể, nhưng tôi cảm thấy như nó sẽ làm mọi thứ chậm lại và sử dụng nhiều bộ nhớ thêm. MyInt thực sự không thể được sử dụng bên ngoài cấu trúc heap. Nhưng cảm ơn bạn đã gõ một ví dụ, thật thú vị để xem.
Leo Ufimtsev

Hừ! Một ngày sau khi tôi nhận xét tôi đã gặp phải tình huống tôi cần đặt một đối tượng tùy chỉnh vào một đống và cần một đống tối đa. Tôi thực sự đã googled bài đăng này và tìm thấy câu trả lời của bạn và dựa trên giải pháp của tôi về nó. (Đối tượng tùy chỉnh là một Điểm có tọa độ x, y và lt ghi đè so sánh khoảng cách từ tâm). Cảm ơn bạn đã đăng bài này, tôi ủng hộ!
Leo Ufimtsev

1

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

0

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]

1
Thật không may, độ phức tạp thời gian cho điều này là O (MlogM) trong đó M = len (nums), đánh bại mục đích của heapq. Xem cách triển khai và nhận xét nlargesttại đây -> github.com/python/cpython/blob/ trên
Arthur S

1
Cảm ơn bạn đã bình luận thông tin của bạn, sẽ đảm bảo kiểm tra các liên kết đính kèm.
RowanX

0

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)

0

Để 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


0

Đây là một MaxHeapthự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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.