heapq的唯一问题是它没有提供像stdlib中其他所有功能一样的关键功能. (如果你好奇为什么,Raymond Hettinger在
this email中解释过.他是对的,heapq无法提供与其他排序函数相同的接口 – 但原因不会影响你的用例,其中key只是lambda x: – X.)
通常的解决方法是decorate-heap-undecorate.也就是说,将值的修改版本放入按密钥排序的堆中.通常,这意味着以下之一:
>存储密钥(x)而不是x,然后访问unkey(值)而不是值(假设密钥是可逆的).
>存储(key(x),x)而不是x,然后访问值[1]. (这可以打破稳定性,但无论如何,heapq都不会保证稳定性.)
>编写一个实现自定义__le__方法的包装类,然后存储Wrapper(x)而不是x并访问value.value而不是value.
在您的情况下,关键功能是可逆的.所以,只需存储-x和access -value.这和装饰一样微不足道.
尽管如此,无论它有多简单,你都应该写一个包装器,否则你会在某些时候搞砸它.例如,您可以编写一个maxheap来包装heapq中的minheap,如下所示:
import heapq
def heapify(x):
for i in range(len(x)):
x[i] = -x[i]
heapq.heapify(x)
def heappush(heap, item):
heapq.heappush(heap, -item)
def heappop(heap):
return -heapq.heappop(heap)
……等等您需要的任何其他功能.这可能有点痛苦,但是比从头开始实施整个事情要少得多.
当你在它时,你可能想要将堆包装在面向对象的API中,这样你就可以执行heap.push(x)而不是heapq.heappush(heap,x)等.
import heapq
class MaxHeap(object):
def __init__(self, x):
self.heap = [-e for e in x]
heapq.heapify(self.heap)
def push(self, value):
heapq.heappush(self.heap, -value)
def pop(self):
return -heapq.heappop(self.heap)
…
如果您快速浏览一下ActiveState的配方或PyPI上的模块,您会发现其他人已经为您完成了大部分工作.
或者,您可以将heapq源(它是纯Python)复制并粘贴为maxheapq.py,然后将cmp_lt函数替换为相反的cmp_lt函数. (当然,如果你正在这样做,那么修改cmp_lt以获取一个关键参数,并且修改所有其他函数以传递密钥时,可能同样容易,当然也更清楚一些.它将不再普遍适用,因为它不能通常保证只调用一次密钥.)
如果你真的想要危险地生活(你不应该),你甚至可以将其打包:
import heapq
def cmp_gt(x, y):
return y < x if hasattr(y, '__lt__') else not (x <= y)
heapq.cmp_lt = cmp_gt
但是你不想在实际代码中这样做.