사용자 지정 정렬 조건 자로 힙을 만들려고합니다. 여기에 들어가는 값은 '사용자 정의'유형이므로 내장 된 비교 술어를 수정할 수 없습니다.
다음과 같은 방법이 있습니까?
h = heapq.heapify([...], key=my_lt_pred)
h = heapq.heappush(h, key=my_lt_pred)
또는 더 좋은 점은 내 컨테이너에 heapq 함수를 래핑하여 술어를 계속 전달할 필요가 없다는 것입니다.
사용자 지정 정렬 조건 자로 힙을 만들려고합니다. 여기에 들어가는 값은 '사용자 정의'유형이므로 내장 된 비교 술어를 수정할 수 없습니다.
다음과 같은 방법이 있습니까?
h = heapq.heapify([...], key=my_lt_pred)
h = heapq.heappush(h, key=my_lt_pred)
또는 더 좋은 점은 내 컨테이너에 heapq 함수를 래핑하여 술어를 계속 전달할 필요가 없다는 것입니다.
답변:
heapq 문서 에 따르면 힙 순서를 사용자 정의하는 방법은 힙의 각 요소가 튜플이되도록하는 것입니다. 첫 번째 튜플 요소는 일반적인 Python 비교를 허용하는 요소입니다.
heapq 모듈의 함수는 약간 번거롭고 (객체 지향적이지 않기 때문에) 항상 첫 번째 매개 변수로 명시 적으로 전달되는 힙 객체 (힙화 된 목록)가 필요합니다. 하나의 돌로 두 마리의 새를 죽일 수 있습니다.key
함수 하고 힙을 객체로 .
아래 클래스는 내부 목록을 유지합니다. 여기서 각 요소는 튜플이며 첫 번째 멤버는 key
매개 변수를 사용하여 요소 삽입시 계산 되며 힙 인스턴스화에서 전달됩니다.
# -*- coding: utf-8 -*-
import heapq
class MyHeap(object):
def __init__(self, initial=None, key=lambda x:x):
self.key = key
self.index = 0
if initial:
self._data = [(key(item), i, item) for i, item in enumerate(initial)]
self.index = len(self._data)
heapq.heapify(self._data)
else:
self._data = []
def push(self, item):
heapq.heappush(self._data, (self.key(item), self.index, item))
self.index += 1
def pop(self):
return heapq.heappop(self._data)[2]
(추가 self.index
부분은 평가 된 키 값이 그리기이고 저장된 값이 직접 비교할 수 없을 때 충돌을 방지하는 것입니다. 그렇지 않으면 heapq가 TypeError로 실패 할 수 있습니다)
id(item)
관계를 끊기 위해 튜플의 중간 요소로 넣었습니다 .
__lt__()
함수 를 재정의하는 클래스를 정의 합니다. 아래 예를 참조하십시오 (Python 3.7에서 작동).
import heapq
class Node(object):
def __init__(self, val: int):
self.val = val
def __repr__(self):
return f'Node value: {self.val}'
def __lt__(self, other):
return self.val < other.val
heap = [Node(2), Node(0), Node(1), Node(4), Node(2)]
heapq.heapify(heap)
print(heap) # output: [Node value: 0, Node value: 2, Node value: 1, Node value: 4, Node value: 2]
heapq.heappop(heap)
print(heap) # output: [Node value: 1, Node value: 2, Node value: 2, Node value: 4]
__gt__
대신 사용하여 테스트 했으며 잘 작동합니다. 우리가 사용하는 마법 방법이 왜 중요하지 않습니까? heapq
의 문서 에서 아무것도 찾을 수 없습니다 . 아마도 파이썬이 일반적으로 비교하는 방법과 관련이 있습니까?
heapq
Python은 __lt__()
먼저 찾습니다 . 정의되지 않은 경우 __gt__()
. 둘 다 정의되지 않은 경우 TypeError: '<' not supported between instances of 'Node' and 'Node'
. 이는 __lt__()
및 둘 다 정의 __gt__()
하고 각각에 print 문을 배치하고 __lt__()
return을 사용 하여 확인할 수 있습니다 NotImplemented
.
heapq 문서는 힙 요소는 첫 번째 요소는 우선 순위 및 정렬 순서를 정의하는 튜플이 될 수 있음을 시사한다.
그러나 귀하의 질문과 더 관련이 있다는 것은 문서에 자체 heapq 래퍼 함수를 구현하여 정렬 안정성 및 동일한 우선 순위를 가진 요소 (다른 문제 중에서) 문제를 처리하는 방법에 대한 샘플 코드 에 대한 토론이 포함되어 있다는 것입니다.
요컨대, 그들의 해결책은 heapq의 각 요소가 우선 순위, 항목 수 및 삽입 할 요소가있는 트리플이되도록하는 것입니다. 항목 수는 같은 우선 순위를 가진 요소가 힙에 추가 된 순서대로 정렬되도록합니다.
두 답변의 한계는 동점이 동점으로 취급되는 것을 허용하지 않는다는 것입니다. 첫 번째는 항목을 비교하여 연결을 끊고 두 번째는 입력 순서를 비교하여 연결합니다. 동점을 묶어 두는 것이 더 빠르며, 많은 경우 큰 차이를 만들 수 있습니다. 위와 문서를 기반으로 이것이 heapq에서 달성 될 수 있는지 명확하지 않습니다. heapq가 키를 받아들이지 않는 반면 동일한 모듈에서 파생 된 함수는 키를 받아들이지 않는 것이 이상해 보입니다.
추신 : 첫 번째 주석 ( "중복 가능성 ...")의 링크를 따라 가면 해결책처럼 보이는 파일을 정의하는 또 다른 제안이 있습니다.