heapq 提供的 nsmallest 和 nlargest 方法不假定传递给它们的参数已经是堆格式。相反,他们在遍历参数时寻求“堆积”参数,这将比对小 k 值的前 k 个元素进行直接排序更有效,但对于 k 正好等于 1,避免支付heapify-as-you-traverse 开销,直接使用min。
你的说法是正确的。如果给定一个数组,您可以保证该数组已被堆化,并且此后未更改,那么访问第一个元素将为您提供最小值(分别为最大堆的最大值)。
查看source code for heapq(也许我正在查看旧代码?)对我来说仍然很奇怪。 nsmallest 有一个 n ==1 的特殊情况,像这样实现(第 397 行):
def nsmallest(n, iterable, key=None):
"""Find the n smallest elements in a dataset.
Equivalent to: sorted(iterable, key=key)[:n]
"""
# Short-cut for n==1 is to use min() when len(iterable)>0
if n == 1:
it = iter(iterable)
head = list(islice(it, 1))
if not head:
return []
if key is None:
return [min(chain(head, it))]
return [min(chain(head, it), key=key)]
# ... rest of function
只是在解释器中玩弄那个表达式就会让人觉得很奇怪:
In [203]: foo = list(itertools.islice([1,2,3], 1)); it = iter([1,2,3]); x = itertools.chain(foo, it);
In [204]: x.next()
Out[204]: 1
In [205]: x.next()
Out[205]: 1
In [206]: x.next()
Out[206]: 2
In [207]: x.next()
Out[207]: 3
In [208]: x.next()
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-208-e05f366da090> in <module>()
----> 1 x.next()
StopIteration:
它似乎正在构建一个生成器(它立即变成一个list),它只使用第一个元素(正如你可能期望的那样使用最小堆),但奇怪的是chains 它只是一个普通的将遍历整个数组的旧生成器。
我同意,如果您从list 开始并想要查询最小元素,最好将其保留为list 并使用min。但是,如果您得到一个最小堆,是的,您确实应该只检查第一个元素——这是首先将它堆起来的一部分。
但无论如何,这个源代码对于将最小堆传递给min 看起来很奇怪——我非常欢迎更多关于它在做什么的解释——也许是指向一些更新的 C 级代码的指针以用于实现heapq,如果有的话。