从包含100,000个整数的列表中检索两个最高项

Joe*_*oey 37 python sorting list

如何从包含100,000个整数的列表中检索两个最高项,而不必先排序整个列表?

Fog*_*ird 55

在Python中,使用heapq.nlargest.如果您想要处理的不仅仅是前两个元素,这是最灵活的方法.

这是一个例子.

>>> import heapq
>>> import random
>>> x = range(100000)
>>> random.shuffle(x)
>>> heapq.nlargest(2, x)
[99999, 99998]
Run Code Online (Sandbox Code Playgroud)

文档:http: //docs.python.org/library/heapq.html#heapq.nlargest

  • 我运行了一个测试,发现nlargest的速度是使用长度为1000的混洗列表排序的两倍.(`nlargest(2,x)`vs`sorted(x,reverse = True)[:2]`) (4认同)
  • 抱歉,我太小气了,但这并不能真正回答问题。OP 特别要求提供一种无需对列表进行排序的解决方案 - 而 heapq.nlargest 的文档明确表示它相当于排序。 (2认同)
  • @Korem它提供等效的*结果*。 (2认同)
  • @Korem我相信它实际上是O(nlogk),其中k在这种情况下是2。(堆仅达到这个大小。) (2认同)
  • @FogleBird 这很有趣。我很高兴我发表了评论,我学到了一些新东西。 (2认同)

Wes*_*ley 16

JacobM的答案绝对是可行的方法.但是,在实现他描述的内容时,需要记住一些事项.这里有一个小小的家庭教程,指导您解决这个问题的棘手部分.

如果此代码仅供生产使用,请使用列出的更有效/简洁的答案之一.这个答案针对的是编程新手.

这个想法

这个想法很简单.

  • 保留两个变量:largestsecond_largest.
  • 浏览列表.
    • 如果项目大于largest,则将其分配给largest.
    • 如果项目大于second_largest但小于largest,则将其分配给second_largest.

入门

开始吧.

def two_largest(inlist):
    """Return the two largest items in the sequence. The sequence must
    contain at least two items."""
    for item in inlist:
        if item > largest:
            largest = item
        elif largest > item > second_largest:
            second_largest = item
    # Return the results as a tuple
    return largest, second_largest

# If we run this script, it will should find the two largest items and
# print those
if __name__ == "__main__":
    inlist = [3, 2, 1]
    print two_largest(inlist)
Run Code Online (Sandbox Code Playgroud)

好的,我们现在将JacobM的答案作为Python函数.当我们尝试运行它时会发生什么?

Traceback (most recent call last):
  File "twol.py", line 10, in <module>
    print two_largest(inlist)
  File "twol.py", line 3, in two_largest
    if item > largest:
UnboundLocalError: local variable 'largest' referenced before assignment
Run Code Online (Sandbox Code Playgroud)

显然,我们需要largest在开始循环之前设置.这可能意味着我们也应该second_largest这样做.

初始化变量

让我们设置largestsecond_largest为0.

def two_largest(inlist):
    """Return the two largest items in the sequence. The sequence must
    contain at least two items."""
    largest = 0 # NEW!
    second_largest = 0 # NEW!
    for item in inlist:
        if item > largest:
            largest = item
        elif largest > item > second_largest:
            second_largest = item
    # Return the results as a tuple
    return largest, second_largest

# If we run this script, it will should find the two largest items and
# print those
if __name__ == "__main__":
    inlist = [3, 2, 1]
    print two_largest(inlist)
Run Code Online (Sandbox Code Playgroud)

好.我们来吧吧.

(3, 2)
Run Code Online (Sandbox Code Playgroud)

大!现在,让我们用测试inlist[1, 2, 3]

    inlist = [1, 2, 3] # CHANGED!
Run Code Online (Sandbox Code Playgroud)

我们来试试吧.

(3, 0)
Run Code Online (Sandbox Code Playgroud)

......哦,哦.

修复逻辑

最大值(3)似乎是正确的.但第二大值完全错误.这是怎么回事?

让我们来看看函数正在做什么.

  • 当我们开始时,largest是0并且second_largest也是0.
  • 我们看到的列表中的第一项是1,因此largest变为1.
  • 下一项是2,所以largest变为2.

但那怎么样second_largest

当我们为其分配新值时largest,最大值实际上变为第二大值.我们需要在代码中显示.

def two_largest(inlist):
    """Return the two largest items in the sequence. The sequence must
    contain at least two items."""
    largest = 0
    second_largest = 0
    for item in inlist:
        if item > largest:
            second_largest = largest # NEW!
            largest = item
        elif largest > item > second_largest:
            second_largest = item
    # Return the results as a tuple
    return largest, second_largest

# If we run this script, it will should find the two largest items and
# print those
if __name__ == "__main__":
    inlist = [1, 2, 3]
    print two_largest(inlist)
Run Code Online (Sandbox Code Playgroud)

我们来吧吧.

(3, 2)
Run Code Online (Sandbox Code Playgroud)

太棒了.

初始化变量,第2部分

现在让我们尝试一下负数列表.

    inlist = [-1, -2, -3] # CHANGED!
Run Code Online (Sandbox Code Playgroud)

我们来吧吧.

(0, 0)
Run Code Online (Sandbox Code Playgroud)

这根本不对.这些零来自哪里?

事实证明,起点值largestsecond_largest实际上比列表中的所有项目大.您可能会考虑的第一件事是在Python中设置largestsecond_largest尽可能低的值.不幸的是,Python没有尽可能小的价值.这意味着,即使您将它们都设置为-1,000,000,000,000,000,000,您也可以拥有一个小于该值的列表.

那么最好的做法是什么?让我们尝试设置largestsecond_largest在列表中的第一项,第二项.然后,为了避免重复计算列表中的任何项目,我们只在第二项之后查看列表中的部分.

def two_largest(inlist):
    """Return the two largest items in the sequence. The sequence must
    contain at least two items."""
    largest = inlist[0] # CHANGED!
    second_largest = inlist[1] # CHANGED!
    # Only look at the part of inlist starting with item 2
    for item in inlist[2:]: # CHANGED!
        if item > largest:
            second_largest = largest
            largest = item
        elif largest > item > second_largest:
            second_largest = item
    # Return the results as a tuple
    return largest, second_largest

# If we run this script, it will should find the two largest items and
# print those
if __name__ == "__main__":
    inlist = [-1, -2, -3]
    print two_largest(inlist)
Run Code Online (Sandbox Code Playgroud)

我们来吧吧.

(-1, -2)
Run Code Online (Sandbox Code Playgroud)

大!让我们尝试另一个负数列表.

    inlist = [-3, -2, -1] # CHANGED!
Run Code Online (Sandbox Code Playgroud)

我们来吧吧.

(-1, -3)
Run Code Online (Sandbox Code Playgroud)

等等,什么?

初始化变量,第3部分

让我们再次逐步完善我们的逻辑.

  • largest 设置为-3
  • second_largest 设置为-2

在那儿等一下 这似乎是错的.-2大于-3.这是什么原因造成的?我们继续吧.

  • largest设置为-1; second_largest设置为旧值largest,即-3

是的,这看起来是个问题.我们需要确保largestsecond_largest正确设置.

def two_largest(inlist):
    """Return the two largest items in the sequence. The sequence must
    contain at least two items."""
    if inlist[0] > inlist[1]: # NEW
        largest = inlist[0]
        second_largest = inlist[1]
    else: # NEW
        largest = inlist[1] # NEW
        second_largest = inlist[0] # NEW
    # Only look at the part of inlist starting with item 2
    for item in inlist[2:]:
        if item > largest:
            second_largest = largest
            largest = item
        elif largest > item > second_largest:
            second_largest = item
    # Return the results as a tuple
    return largest, second_largest

# If we run this script, it will should find the two largest items and
# print those
if __name__ == "__main__":
    inlist = [-3, -2, -1]
    print two_largest(inlist)
Run Code Online (Sandbox Code Playgroud)

我们来吧吧.

(-1, -2)
Run Code Online (Sandbox Code Playgroud)

优秀.

结论

所以这里是代码,很好地评论和格式化.它也有我可以找到的所有错误.请享用.

但是,假设这确实是一个家庭作业问题,我希望你从看到一段不完美的代码慢慢改进中获得一些有用的经验.我希望其中一些技术在将来的编程任务中有用.


效率

不是很有效率.但是对于大多数用途,它应该没问题:在我的计算机(Core 2 Duo)上,可以在0.27秒内处理10万个项目的列表(使用timeit,平均超过100次运行).

  • 如果你的目标是教学,我们应该解释堆积以及为什么它们对这项任务有好处. (3认同)

Jac*_*son 6

您遍历列表,维护包含到目前为止遇到的最高和第二高项的值的变量.遇到的每个新项目将替换新项目高于(如果有)的两个中的任何一项.

  • @kamula前N项的棘手问题是避免将每个新项目与所有前N个项目进行比较.如果N很大,最好的选择可能是将前N个变量存储在某种二叉树中,这样对于每个新项目,您可以快速确定应该替换哪个项目(如果有的话).分别维护到目前为止您看到的最高项目和第N项的变量也是值得的,因此您可以快速判断每个新项目是否需要钻研树(您只需要搜索树如果新项目位于顶部项目和第N个项目之间). (2认同)

zda*_*dav 5

一个非常光滑的方式是使用heapq. 修改数组(O(n)),然后弹出你需要的许多元素(log(n)).(在一次采访中看到这个问题,要记住这个问题.)

  • 如果您了解heapq,您应该知道:http://docs.python.org/library/heapq.html#heapq.nlargest (2认同)