Apa yang saya gunakan untuk implementasi max-heap di Python?

Jawaban:

244

Cara termudah adalah membalikkan nilai kunci dan menggunakan heapq. Misalnya, ubah 1000.0 menjadi -1000.0 dan 5.0 menjadi -5.0.

Daniel Stutzbach
sumber
38
Itu juga solusi standar.
Andrew McGregor
44
uggh; total lumpur. Saya kaget heapqtidak memberikan yang sebaliknya.
shabbychef
40
Wow. Saya kagum bahwa ini tidak disediakan oleh heapq, dan tidak ada alternatif yang baik.
ire_and_curses
23
@gatoatigrado: Jika Anda memiliki sesuatu yang tidak mudah dipetakan ke int/ float, Anda dapat membalikkan pemesanan dengan membungkusnya di kelas dengan __lt__operator yang terbalik .
Daniel Stutzbach
5
@Aerovistae saran yang sama berlaku: membalikkan nilai-nilai (yaitu beralih tanda) terlepas dari apakah positif atau negatif untuk memulai.
Dennis
235

Kamu bisa memakai

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

Jika Anda ingin memunculkan elemen, gunakan:

heapq.heappop(minheap)      # pop from minheap
heapq._heappop_max(maxheap) # pop from maxheap
Lijo Joseph
sumber
34
Tampak seperti ada beberapa fungsi yang tidak terdokumentasi untuk max heap: _heapify_max, _heappushpop_max, _siftdown_max, dan _siftup_max.
ziyuang
127
Wow. Saya kagum bahwa ada IS seperti built-in solusi di heapq. Tetapi kemudian benar-benar tidak masuk akal bahwa BUKAN bahkan sedikit disebutkan sama sekali dalam dokumen resmi! WTF!
RayLuo
27
Salah satu fungsi pop / push memecah struktur heap max, sehingga metode ini tidak layak.
Siddhartha
22
JANGAN GUNAKAN ITU. Seperti yang dilihat LinMa dan Siddhartha, push / pop merusak urutan.
Alex Fedulov
13
Metode yang dimulai dengan garis bawah bersifat pribadi dan dapat dihapus tanpa pemberitahuan sebelumnya . Jangan gunakan itu.
user4815162342
66

Solusinya adalah dengan meniadakan nilai-nilai Anda ketika Anda menyimpannya di heap, atau membalikkan perbandingan objek Anda seperti:

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)

Contoh tumpukan maksimum:

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

Tetapi Anda harus ingat untuk membungkus dan membuka nilai-nilai Anda, yang mengharuskan Anda mengetahui apakah Anda berurusan dengan tumpukan minimum atau maksimum.

MinHeap, kelas MaxHeap

Menambahkan kelas untuk MinHeapdan MaxHeapobjek dapat menyederhanakan kode Anda:

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

Contoh penggunaan:

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"
Isaac Turner
sumber
Bagus. Saya telah mengambil ini dan menambahkan listparameter opsional ke __init__ dalam hal ini saya menelepon heapq.heapifydan juga menambahkan heapreplacemetode.
Booboo
1
Terkejut bahwa tidak ada yang menangkap kesalahan ketik ini: MaxHeapInt -> MaxHeapObj. Kalau tidak, solusi yang sangat bersih memang.
Chiraz BenAbdelkader
@ChirazBenAbdelkader diperbaiki, terima kasih.
Isaac Turner
39

Solusi termudah dan ideal

Lipat gandakan nilainya dengan -1

Ini dia. Semua angka tertinggi sekarang adalah yang terendah dan sebaliknya.

Ingatlah bahwa ketika Anda memunculkan elemen untuk melipatgandakannya dengan -1 untuk mendapatkan nilai asli lagi.

Sebastian Nielsen
sumber
Hebat, tetapi sebagian besar solusi mendukung kelas / tipe lain, dan tidak akan mengubah data aktual. Pertanyaan terbuka adalah jika mengalikan nilai dengan -1 tidak akan mengubahnya (float sangat tepat).
Alex Baranowski
1
@AlexBaranowski. Itu benar, tetapi itu telah menjadi respons dari pengelola: bugs.python.org/issue27295
Flair
Pemelihara yang baik berhak untuk tidak mengimplementasikan beberapa fungsi, tetapi IMO yang satu ini sebenarnya berguna.
Alex Baranowski
7

Saya menerapkan versi heapq max heapq dan mengirimkannya ke PyPI. (Perubahan sangat kecil pada kode heapq modul CPython.)

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

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

Instalasi

pip install heapq_max

Pemakaian

tl; dr: sama dengan modul heapq kecuali menambahkan '_max' ke semua fungsi.

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
Zhe He
sumber
4

Jika Anda memasukkan kunci yang sebanding tetapi tidak seperti int, Anda berpotensi menimpa operator perbandingan pada mereka (yaitu <= menjadi> dan> menjadi <=). Jika tidak, Anda dapat mengganti heapq._siftup di modul heapq (pada akhirnya hanya kode Python).

rlotun
sumber
9
“Itu semua hanya kode Python”: itu tergantung pada versi dan instalasi Python Anda. Sebagai contoh, heapq.py saya yang terinstal memiliki beberapa kode setelah baris 309 ( # If available, use C implementation) yang melakukan persis seperti yang dijelaskan oleh komentar.
tzot
3

Mengizinkan Anda memilih jumlah item terbesar atau terkecil yang sewenang-wenang

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]
jasonleonhard
sumber
3
Penjelasan akan diurutkan.
Peter Mortensen
Judul saya adalah penjelasan saya
jasonleonhard
1
Jawaban saya lebih panjang dari pertanyaan. Penjelasan apa yang ingin Anda tambahkan?
jasonleonhard
2
Ini memberikan hasil yang benar tetapi tidak benar-benar menggunakan tumpukan untuk membuatnya efisien. Doc menentukan bahwa nlargest dan nsmallest mengurutkan daftar setiap kali.
RossFabricant
3

Memperluas kelas int dan mengesampingkan __lt__ adalah salah satu caranya.

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()
Gaurav
sumber
Itu mungkin, tapi saya merasa akan memperlambat banyak hal dan menggunakan banyak memori tambahan. MyInt juga tidak bisa digunakan di luar struktur heap. Tapi terima kasih telah mengetik contoh, menarik untuk dilihat.
Leo Ufimtsev
Hah! Suatu hari setelah saya berkomentar saya berlari ke dalam situasi di mana saya perlu meletakkan objek kustom ke tumpukan dan membutuhkan tumpukan max. Saya sebenarnya telah me-reoge ulang posting ini dan menemukan jawaban Anda dan mendasarkan solusi saya darinya. (Custom object menjadi Point dengan x, y koordinat dan lt override membandingkan jarak dari pusat). Terima kasih telah memposting ini, saya membenarkan!
Leo Ufimtsev
1

Saya telah membuat heap wrapper yang membalikkan nilai untuk membuat max-heap, serta kelas wrapper untuk min-heap untuk membuat perpustakaan lebih seperti OOP. Inilah intinya. Ada tiga kelas; Heap (kelas abstrak), HeapMin, dan HeapMax.

Metode:

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
noɥʇʎԀʎzɐɹƆ
sumber
0

Jika Anda ingin mendapatkan elemen K terbesar menggunakan max heap, Anda bisa melakukan trik berikut:

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]
RowanX
sumber
1
Sayangnya, kompleksitas waktu untuk ini adalah O (MlogM) di mana M = len (nums), yang mengalahkan tujuan heapq. Lihat implementasi dan komentar untuk di nlargestsini -> github.com/python/cpython/blob/…
Arthur S
1
Terima kasih atas komentar informatif Anda, akan memastikan untuk memeriksa tautan terlampir.
RowanX
0

Menindaklanjuti jawaban Isaac Turner yang luar biasa , saya ingin memberikan contoh berdasarkan K Poin Terdekat dengan Asal menggunakan max heap.

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)
Apoorv Patne
sumber
0

Untuk menguraikan tentang https://stackoverflow.com/a/59311063/1328979 , berikut ini adalah implementasi Python 3 yang sepenuhnya didokumentasikan, beranotasi, dan teruji untuk kasus umum.

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

Marc Carré
sumber
0

Ini adalah MaxHeapimplementasi sederhana berdasarkan heapq. Padahal itu hanya bekerja dengan nilai numerik.

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)

Pemakaian:

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