파이썬에서 max-heap 구현에 무엇을 사용합니까?


답변:


241

가장 쉬운 방법은 키 값을 반전시키고 heapq를 사용하는 것입니다. 예를 들어 1000.0을 -1000.0으로, 5.0을 -5.0으로 바꿉니다.


37
또한 표준 솔루션입니다.
Andrew McGregor

43
어; 총 kludge. 나는 heapq역전을 제공하지 않는다는 것에 놀랐다 .
shabbychef

40
와. 나는 이것이 제공 하지 않으며 좋은 대안이 없다는 것에 놀랐습니다heapq .
ire_and_curses 2016 년

23
@gatoatigrado : int/에 쉽게 매핑되지 않는 것이 있으면 float__lt__연산자 로 클래스에 래핑하여 순서를 반전시킬 수 있습니다 .
Daniel Stutzbach

5
@Aerovistae 같은 충고가 적용됩니다 : 시작하기 위해 양수인지 음수인지에 관계없이 값을 반전시킵니다 (예 : 부호 바꾸기).
Dennis

233

당신이 사용할 수있는

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

34
외모가 최대 힙에 대한 몇 가지 문서화되지 않은 함수가 좋아 : _heapify_max, _heappushpop_max, _siftdown_max,와 _siftup_max.
ziyuang

127
와. 난 깜짝 놀라게 가 있다는 IS 와 같은 내장 heapq의 솔루션입니다. 그러나 공식 문서에 전혀 언급 되지 않았다는 것은 완전히 불합리 합니다 ! WTF!
RayLuo

27
팝 / 푸시 기능 중 하나라도 최대 힙 구조를 깨뜨 리므로이 방법을 사용할 수 없습니다.
Siddhartha

22
IT를 사용하지 마십시오. LinMa와 Siddhartha가 알았 듯이 push / pop은 순서를 깨뜨립니다.
Alex Fedulov

13
밑줄로 시작하는 메소드는 개인용 이며 사전 통지없이 제거 할 수 있습니다 . 사용하지 마십시오.
user4815162342

66

해결책은 값을 힙에 저장할 때 값을 부정하거나 다음과 같이 객체 비교를 반전시키는 것입니다.

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)

최대 힙의 예 :

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"

좋은. 나는 이것을 list취하고 __init__에 선택적 매개 변수를 추가 했다.이 경우 호출 heapq.heapify하고 heapreplace메소드를 추가했다 .
Booboo

1
MaxHeapInt-> MaxHeapObj와 같은 오타가 아무도 없다는 사실에 놀랐습니다. 그렇지 않으면 실제로 매우 깨끗한 솔루션입니다.
Chiraz BenAbdelkader

@ChirazBenAbdelkader 고정, 감사합니다.
아이작 터너

39

가장 쉽고 이상적인 솔루션

값에 -1을 곱하십시오

당신은 간다. 가장 높은 숫자는 모두 가장 낮으며 그 반대도 마찬가지입니다.

요소를 팝 할 때 원래 값을 다시 얻기 위해 -1을 곱하는 요소를 기억하십시오.


훌륭하지만 대부분의 솔루션은 클래스 / 다른 유형을 지원하며 실제 데이터는 변경하지 않습니다. 열린 질문은 값에 -1을 곱해도 값이 변경되지 않는 경우입니다 (매우 정확한 부동 소수점).
Alex Baranowski

1
@AlexBaranowski. 사실이지만, 이것은 관리자의 반응 이었습니다
Flair

잘 유지 관리하는 사람은 일부 기능을 구현하지 않을 권리가 있지만이 IMO는 실제로 유용합니다.
Alex Baranowski

7

heapq의 최대 힙 버전을 구현하고 PyPI에 제출했습니다. (heapq 모듈 CPython 코드가 약간 변경되었습니다.)

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

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

설치

pip install heapq_max

용법

tl; dr : 모든 함수에 '_max'를 추가하는 것을 제외하고 heapq 모듈과 동일합니다.

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

비교 가능하지만 int와는 다른 키를 삽입하는 경우 비교 연산자를 대체 할 수 있습니다 (예 : <=가> 및>가 <=가 됨). 그렇지 않으면 heapq 모듈에서 heapq._siftup을 재정의 할 수 있습니다 (결국 파이썬 코드 일뿐입니다).


9
"모두 파이썬 코드 일뿐입니다.": 파이썬 버전과 설치에 따라 다릅니다. 예를 들어, 설치된 heapq.py에는 309 줄 ( # If available, use C implementation) 뒤에 주석이 설명하는 것과 정확히 같은 코드 가 있습니다.
tzot

3

임의의 양의 최대 또는 최소 항목을 선택할 수 있도록 허용

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
순서대로 설명하겠습니다.
피터 Mortensen

나의 제목은 내 설명입니다
jasonleonhard

1
내 대답은 질문보다 깁니다. 어떤 설명을 추가 하시겠습니까?
jasonleonhard


2
이것은 올바른 결과를 제공하지만 실제로 효율적으로 힙을 사용하지는 않습니다. 이 문서는 매번 nlargest 및 nsmallest가 목록을 정렬하도록 지정합니다.
RossFabricant

3

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

가능하지만 많은 것을 느리게하고 여분의 메모리를 많이 사용한다고 생각합니다. MyInt는 힙 구조 외부에서도 실제로 사용할 수 없습니다. 그러나 예제를 입력 해 주셔서 감사합니다. 흥미 롭습니다.
레오 Ufimtsev

하! 댓글을 작성한 후 어느 날 나는 사용자 정의 객체를 힙에 넣고 최대 힙이 필요한 상황에 부딪쳤다. 나는 실제로이 게시물을 다시 검색하고 귀하의 답변을 찾았고 내 솔루션을 기반으로했습니다. (X가 포인트 지정되는 오브젝트가, y 좌표 및 LT 중심으로부터의 거리를 비교하여 무시). 게시 해 주셔서 감사합니다.
Leo Ufimtsev

1

최대 힙을 만들기 위해 값을 반전시키는 힙 랩퍼와 최소 힙을위한 랩퍼 클래스를 만들어 라이브러리를보다 OOP와 비슷하게 만듭니다. 요점은 다음과 같습니다 . 세 가지 수업이 있습니다. 힙 (추상 클래스), 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

0

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]

1
불행하게도, 이것에 대한 시간 복잡도는 O = M (MlogM), 여기서 M = len (nums)이며, 이는 heapq의 목적을 무효화합니다. 에 대한 구현 및 의견을 참조하십시오 nlargest여기를 -> github.com/python/cpython/blob/...
아서 S

1
유익한 의견을 보내 주셔서 감사합니다. 첨부 된 링크를 확인하십시오.
RowanX

0

Isaac Turner의 훌륭한 답변에 이어 max heap을 사용 하여 Origin 에 대한 K Closest Points를 기반으로 예제를 작성하고 싶습니다 .

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

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


0

MaxHeap이는를 기반으로 하는 간단한 구현입니다 heapq. 숫자 값으로 만 작동하지만.

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.