如何从至少包含 100000000 个数字的列表中获取最大的 100 个元素?
- 对整个列表进行排序并在排序列表中取最后 100 个元素是一种方法,但这在内存和时间上都很昂贵。
- 是否有更简单、更 Pythonic 的方法来做到这一点?
- 需要一个函数来完成此操作,而不是一个纯粹的排序。实际上不想浪费时间对不关心的元素进行排序。
- 解决方案
-
heapq 模块:
- Python 标准库中的 heapq 模块提供了 nlargest() 函数来完成此任务。
- top100 = heapq.nlargest(100, iterable [,key])
- 这种方法不会对整个列表进行排序,因此不必浪费时间在不关心的元素上。
-
选择算法:
- 选择算法可以帮助解决此问题。
- 一个非常简单的解决方案是找到第 100 个最大的元素,然后遍历列表挑选出比此元素更大的元素。这将给出 100 个最大的元素。这是列表长度的线性关系;这是最优解。
- 有更复杂的算法。例如,堆非常适合解决此问题。基于堆的算法是 n log k,其中 n 是列表的长度,k 是想要选择的最大元素的数量。
- 维基百科的选择算法页面上讨论了这个问题。
-
堆数据结构:
- 可以使用堆数据结构。堆不一定是顺序的,但它是一种保持半有序数据相当快的方法,并且具有始终将最小项作为堆中第一个元素的好处。
- 堆有两个基本操作将帮助完成此任务:添加和替换。
- 基本上,需要做的就是向其中添加项,直到获得 100 个项(问题中要求的顶部 N 个数字)。然后,将第一个项替换为每个新项,只要新项大于第一个项即可。
- 每当将第一个项替换为更大的项时,堆中的内部代码将调整堆内容,以便如果新项不是最小的,它将冒泡到堆中,而最小的项将“向下冒泡”到第一个元素,准备好沿途替换。
-
维护堆排序优先级队列:
- 最好的方法是维护一个堆排序优先级队列,一旦其中有 100 个条目,就将其弹出。
- 虽然不管结果是否已排序,但直观上很明显可以免费获得它。为了知道已经获得前 100 个,需要通过某种高效的数据结构按顺序排列当前的顶级数字列表。该结构将知道最小值、最大值以及每个元素的相对位置,以某种自然的方式断言它在其邻居旁边的位置。
- 如前所述,在 Python 中可以使用 heapq。在 Java 中使用 PriorityQueue:
- https://java.sun.com/javase/6/docs/api/java/util/PriorityQueue.html
-
独立于库的解决方案:
- 这是曾经使用过且独立于库的解决方案,它可以在具有数组的任何编程语言中工作:
- 初始化:创建一个包含 100 个元素的数组,并用低值(小于输入列表中的任何值)初始化所有元素。
- 初始化一个整数变量为 0(或 [0;99] 中的任何值),比如 index_minvalue,它将指向数组中的当前最低值。
- 初始化变量 minvalue 来保存数组中的当前最低值。
- 对于输入列表中的每个值(比如 current_value):
- 如果 current_value > minvalue
- 用 current_value 替换由 index_minvalue 指向的数组中的值
- 找到数组中的新最低值并将 index_minvalue 设置为其数组索引。(这种线性搜索是可以接受的,因为数组很快就会被大量值填满)
- 将 minvalue 设置为 current_value
- 否则 <什么都不做!>
- 如果 current_value > minvalue
- minvalue 会快速获得一个高值,因此输入列表中的大多数值只需要与 minvalue 进行比较(比较结果大部分将为 false)。
-
Hoare’s 算法的简单变体:
- 对于观众中的算法狂热分子:可以使用 Tony Hoare’s 算法 Find 的简单变体来做到这一点:
- find(topn, a, i, j)
- 从 a[i…j] 中随机选取一个元素 x
- 将子数组 a[i…j] 分区(就像在快速排序中一样)
- 分成比 x 小、等于 x 和大于 x 的元素的子数组
- 让 k 是元素 x 的位置
- 如果 k == 0 则完成
- 如果 k > topn,则调用 find(topn, a, i, k)
- 如果 k < topn,则调用 find(topn-k, k, j)
- 此算法将最大的 topn 个元素放入数组 a 的前 topn 个元素中,而无需对它们进行排序。当然,如果想要对它们进行排序或为了简单起见,堆更好,调用库函数仍然更好。但这是一个很酷的算法。
-
5万+

被折叠的 条评论
为什么被折叠?



