如何从包含100,000个整数的列表中检索两个最高项,而不必先对整个列表进行排序?
答案 0 :(得分:55)
在Python中,使用heapq.nlargest
。如果您想要处理的不仅仅是前两个元素,这是最灵活的方法。
这是一个例子。
>>> import heapq
>>> import random
>>> x = range(100000)
>>> random.shuffle(x)
>>> heapq.nlargest(2, x)
[99999, 99998]
文档: http://docs.python.org/library/heapq.html#heapq.nlargest
答案 1 :(得分:16)
JacobM's answer绝对是最佳选择。但是,在实现他描述的内容时,需要记住一些事项。这里有一个小小的家庭教程,指导您解决解决此问题的棘手部分。
如果此代码仅供生产使用,请使用列出的更有效/简洁的答案之一。这个答案针对的是编程新手。
这个想法很简单。
largest
和second_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)
好的,我们现在将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
显然,我们需要在开始循环之前设置largest
。这可能意味着我们也应该设置second_largest
。
我们将largest
和second_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)
好。我们来吧吧。
(3, 2)
大!现在让我们测试inlist
为[1, 2, 3]
inlist = [1, 2, 3] # CHANGED!
我们试一试。
(3, 0)
......呃哦。
最大值(3)似乎是正确的。但第二大值完全错误。发生了什么事?
让我们完成函数的工作。
largest
为0,second_largest
也为0. largest
变为1。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)
让我们运行它。
(3, 2)
优秀。
现在让我们尝试一下负数列表。
inlist = [-1, -2, -3] # CHANGED!
让我们运行它。
(0, 0)
这根本不对。这些零来自何处?
事实证明largest
和second_largest
的起始值实际上大于列表中的所有项目。您可以考虑的第一件事是将largest
和second_largest
设置为Python中可能的最低值。不幸的是,Python没有尽可能小的价值。这意味着,即使您将它们都设置为-1,000,000,000,000,000,000,您也可以拥有一个小于该值的列表。
那么最好的做法是什么?我们尝试将largest
和second_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)
让我们运行它。
(-1, -2)
大!让我们尝试另一个负数列表。
inlist = [-3, -2, -1] # CHANGED!
让我们运行它。
(-1, -3)
等等,什么?
让我们再次介绍我们的逻辑。
largest
设置为-3 second_largest
设置为-2 等一下。这似乎是错的。 -2大于-3。这是什么原因造成的?让我们继续。
largest
设置为-1; second_largest
设置为largest
的旧值,即-3 是的,这看起来是个问题。我们需要确保正确设置largest
和second_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)
让我们运行它。
(-1, -2)
优异。
所以这里是代码,很好地评论和格式化。它也有我可以找到的所有错误。享受。
然而,假设这确实是一个功课问题,我希望你能从看到一段不完美的代码慢慢改进中获得一些有用的经验。我希望其中一些技术在将来的编程任务中有用。
效率不高。但是对于大多数用途,它应该没问题:在我的计算机(Core 2 Duo)上,可以在0.27秒内处理10万个项目的列表(使用timeit
,平均超过100次)。
答案 2 :(得分:6)
迭代列表,维护包含到目前为止遇到的最高和第二高项的值的变量。遇到的每个新项目将替换新项目高于(如果有)的两个中的任何一个。
答案 3 :(得分:5)
一种非常流畅的方式是使用heapq
。 Heapify the array(O(n)),然后只弹出你需要的许多元素(log(n))。 (在一次采访中看到这个问题,要记住这个问题。)
答案 4 :(得分:2)
“2最高”是不可能的;只有一个项目可以“最高”。也许你的意思是“最高2”。在任何情况下,您都需要说明当列表包含重复项时要执行的操作。您对[8,9,10,10]:(10,9)还是(10,10)有什么要求?如果你的回答是(10,10),请考虑[8,9,10,10,10]的输入。当你得到“最高的两个”时你打算做什么?请编辑您的问题以提供此指导。
与此同时,这是一个采用第一种方法(两个唯一值)的答案:
largest = max(inlist)
second_largest = max(item for item in inlist if item < largest)
您应该在列表中添加少于2个唯一值的警卫。
答案 5 :(得分:1)
这样可行,但我不知道您是否要保留列表中的项目:
max1 = max(myList)
myList.remove(max1)
max2 = max(myList)
如果你这样做,你可以这样做:
max1 = max(myList)
idx1 = myList.index(max1)
myList.pop(idx1)
max2 = max(myList)
myList.insert(idx1,max1)
答案 6 :(得分:1)
将List
复制到List_copy
。
检索最高值并通过以下方式获取其位置:
Highest_value = max(List_copy)
Highest_position = List_copy.index(max(List_copy))
将0
分配给Highest_value
。
List_copy[Highest_position] = 0
再次开始你的行。
Second_Highest = max(List_copy)
答案 7 :(得分:0)
在没有排序的情况下,迭代整个列表是唯一的方法。
答案 8 :(得分:0)
如果不对列表进行排序,真正做到这一点的唯一方法是遍历整个列表并保存最高的两个数字。我认为你最好对名单进行排序。
答案 9 :(得分:0)
第二高的项目是一个相当简单的案例,但对于第k个最高项目,您想要的是selection algorithm。该页面非常详尽,因此最好只阅读它。
答案 10 :(得分:0)
您可以期待的最佳时间是线性的,因为您必须至少查看所有元素。
这是解决问题的伪代码:
//assume list has at least 2 elements
(max, nextMax) = if (list[0] > list[1])
then (list[0], list[1])
else (list[1], list[0])
for (2 <= i < length) {
(max, nextMax) = if (max < list[i]) => (list[i], max)
elseif (nextMax < list[i]) => (max, list[i])
else (no change) => (max, nextMax)
}
return (max, nextMax)
答案 11 :(得分:0)
我知道这个主题已经过时了,但这是解决这个问题的简单方法。针对heapq.nlargest进行测试,这有点快(不需要排序):
适用于正数和负数。
以下功能:使用的最长时间:0.12,最大使用内存:29290496 heapq.nlargest:使用的最长时间:0.14,使用的最大内存:31088640
def two_highest_numbers(list_to_work):
first = None
second = None
for number in list_to_work:
if first is None:
first = number
elif number > first:
second = first
first = number
else:
if second is None:
second = number
elif number > second:
second = number
return [first, second]
答案 12 :(得分:0)
另一种仅使用基本Python函数的解决方案如下所示:
>>> largest = max(lst)
>>> maxIndex = lst.index(largest)
>>> secondLargest = max(max(lst[:maxIndex]), max(lst[maxIndex+1:]))
如果我们将列表分成最大数字,我们知道第二大数字在左半部分或右半部分。因此,我们可以通过简单地找到列表左半部分和右半部分中较大的数字来找到第二大数字。
显示这是O(n)时间和O(1)空间是微不足道的。我们遍历列表一次找到最大元素,然后再次找到第二大元素。我们只存储最大值本身和最大值的索引。
答案 13 :(得分:0)
对列表进行排序,如果list不为null,则提取最后两个元素
>>> a=[0,6,8,5,10,5]
>>> a.sort()
>>> a
[0, 5, 5, 6, 8, 10]
>>> if a:
... print a[-1],a[-2]
...
10 8
简单而有效:)
现在如果不需要排序,请找到max,remove max,再次找到max
>>> a=[0,6,8,5,10,5]
>>> max(a)
10
>>> a.remove(max(a))
>>> max(a)
8
>>>
当然,您将丢失原始列表,但您也可以创建一个临时列表。
答案 14 :(得分:0)
my_list = [20, 1, 9, 5, 10, 3, 4, 2, 11, 21, 2]
max2 = 0
max1 = 0
for i in my_list:
if i > max1:
max1 = i
elif max2 < i < max1:
max2 = i
print(f'max1: {max1}; max2: {max2}')
max1: 21; max2: 11