【问题标题】:Retrieving minimum value from a Python heapq从 Python heapq 中检索最小值
【发布时间】:2014-12-31 19:44:40
【问题描述】:

来自Python docs

后两个函数 [heapq.nlargest 和 heapq.nsmallest] 对于较小的 n 值表现最好。为了 值越大,使用 sorted() 函数效率更高。 此外,当 n==1 时,使用内置的 min() 和 max() 函数。

如果我想检索最小堆中的最小元素,为什么 Python 文档建议使用 min(),我假设它在 O(n) 时间内运行,当 我可以在 O(1) 时间内检索堆中的第一个元素吗? (我假设堆中的第一个元素是最小值)

【问题讨论】:

  • 当您只想要最小值或最大值时,构建堆会产生开销......因此,除非您已经存在 heapq 对象,否则使用 min/max 而不是构建一个来获得这些对象会更有效值。
  • 哦,我明白了。但是如果我已经有一个 heapq 对象,我是否认为检索最小元素的最有效方法是只检索堆的第一个元素?

标签: python heap


【解决方案1】:

heapq 提供的 nsmallestnlargest 方法不假定传递给它们的参数已经是堆格式。相反,他们在遍历参数时寻求“堆积”参数,这将比对小 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,如果有的话。

【讨论】:

  • 您发布的链接显示的代码与您为 nsmallest 发布的代码不同。
  • 第 397 行。不是之前的。
  • 使用itertools.chain的目的是为了处理iterable是一个迭代器的可能性。代码head = list(islice(it, 1)) 用于将空迭代器作为特殊情况处理。
  • foo = list(itertools.islice([1,2,3], 1)); it = iter([1,2,3]); x = itertools.chain(foo, it); 在 islice 之后定义 itnsmallest中的代码更像it = iter([1,2,3]); foo = list(itertools.islice(it, 1)); x = itertools.chain(foo, it);,也就是说islice前进了it,所以x中的第一个值不重复。
  • 明白了,这是有道理的。
【解决方案2】:

如果您只需要在堆积列表中选择一个最小元素,只需执行 list[0]:

import heapq
lst = [1,-1,100,200]
heapq.heapify(lst)

min_value = lst[0]

上面的文档是指获取 n 个最小的数字,如果 n 很大,堆并不是最有效的数据结构。

【讨论】:

    猜你喜欢
    • 2012-09-22
    • 1970-01-01
    • 2014-10-19
    • 1970-01-01
    • 2014-10-08
    • 2016-02-25
    • 1970-01-01
    • 2018-07-09
    • 1970-01-01
    相关资源
    最近更新 更多