从一组N个数(N> X)中提取X个最大数的排序列表的最佳算法是什么。对于大多数算法,我们可以在O(NlogN)
时间内完成。但是有可能做得更好吗?例如,使用二叉树:O(NLogX)
?集合中的数字是完全随机的。
答案 0 :(得分:5)
使用大小为X的min heap。
将第一个 X 元素插入堆中。从元素 X +1开始(称之为 e ),将其与堆顶部 m (目前为止的最小值)进行比较。请注意,此比较将在恒定时间内完成。如果 e > m ,然后 e 值得进入(提取 m 并插入 e )。为集合中的每个元素执行此操作。在此过程结束时,您的堆包含 X 最大的数字。然后 extract-min X 次将为您提供您期望的排序列表。
每个 N 迭代执行潜在的O(lgX)
提取/插入操作,因此第一步是O(NlgX)
时间。然后,你的最小堆中的 X extract-min 的成本将只是O(XlgX)
,这使我们的整体复杂度为O(NlgX)
。
答案 1 :(得分:2)
“最佳”算法取决于许多因素,包括数据集的大小,是否可以修改原始列表以及要选择的项目数。
例如,如果你有10个项目并且你正在寻找最大的三个项目,那么只需对列表进行排序并选择最后三个项目是微不足道的。调用Quickselect重新排序数组以使三个最大的数组位于前面,然后对这三个进行排序可能会更快。但是你节省的少量时间不太可能增加复杂性。
如果你想从10,000中选择前1000名,那么你肯定想要使用Quickselect。 Quickselect是O(n),而Quicksort是O(n log n)。但Quicksort和Quickselect都修改了原始列表。
如果您无法修改原始列表,或者无法将整个列表保存在内存中,那么前一个答案中提到的堆选择算法是最好的方法。
此外,即使Quickselect为O(n)且堆选择算法为O(n log x),当您选择很小比例的项目时,堆选择算法仍然可以比Quickselect更快。例如,如果您想要1,000,000中的前100名,则堆选择算法将更快。我前一段时间在博客上写了一篇相当详细的文章。请参阅When theory meets practice。
答案 2 :(得分:0)
如果X很小,那么插入排序可能就好了。
想想"前10名得分"在视频游戏上列出。玩游戏后,列表会发生什么?除非你的分数高于第10名的分数,否则没有任何反应:你的分数太低而无法列出。但是,如果您的分数足够高以制作列表,那么插入排序会将其放在列表中的适当位置,将所有较低分数移开以便为其腾出空间。在后一种情况下,旧的第10名分数成为新的第11名分数并从名单上删除。