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.


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

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"

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.


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

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


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]

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

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

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]

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)

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


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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.