给定 n 个数字的最小和最大 10 个数字

Posted

技术标签:

【中文标题】给定 n 个数字的最小和最大 10 个数字【英文标题】:Minimum and maximum 10 numbers from given n numbers 【发布时间】:2015-12-09 21:56:37 【问题描述】:

您能否建议一种有效的算法来从一组给定的 n 个数字(未排序)中找到最小 10 个和最大 10 个数字?

我想到的一种方法是对数组进行排序然后选择。

应该有更好的方法来做到这一点。

你能建议一种方法吗?

这不是家庭作业问题。

【问题讨论】:

@Claudiu 从技术上讲,我认为 OP 只能使用部分排序算法。该链接涵盖了它,但选择只处理k-th 最大的数字,而不是top-k 项目。虽然前者可以简化为后者,但我不确定是否可以在不放弃复杂性的情况下反过来。 【参考方案1】:

如果你使用 java 你可以使用 Treemap http://java.sun.com/javase/6/docs/api/java/util/TreeMap.html

可以对键的顺序进行排序,并且在对键进行迭代时,您可以期望它们是按顺序排列的。

时间复杂度为 O(n)

【讨论】:

建议的算法是什么?【参考方案2】:

您可以使用here 解释的快速选择算法从未排序的整数数组中找到第 k 个最大的数。之后,您可以再迭代一次数组并检查大于第 k 个最大元素的元素。因此,在两次迭代中,您可以找到前 k 个元素。同样,您可以应用这种方法来找到最小的 k 个元素。

平均情况下,选择排序算法的时间复杂度为 O(n),其中 n 是数组中元素的数量。第二次遍历数组也需要 O(n) 时间。因此总体复杂度也将是 O(n)。

此算法将比使用堆的方法运行得更快。因为使用这种方法时间复杂度将是 O(nlogk)。

【讨论】:

【参考方案3】:

Python 标准库已经解决了这个问题(heapq.nlargest 和 heapq.smallest)。

对于您的情况,可以使用数据集的前 10 个成员预先填充最小堆和最大堆,然后对数据进行一次传递,并根据需要更新堆:

FOR element IN remaining_data
    IF element > top_of_min_heap
    THEN update_min_heap(element)
    ENDIF

    IF element < top_of_max_heap
    THEN update_max_heap(element)
    ENDIF
ENDFOR

更新步骤替换了现有的最小的十个最大的已经看到和最大的十个最小的已经看到。

Python 标准库代码大致如下:

def nlargest(n, iterable):
    """Find the n largest elements in a dataset.                                                                                 

    Equivalent to:  sorted(iterable, reverse=True)[:n]                                                                           
    """
    if n < 0:
        return []
    it = iter(iterable)
    result = list(islice(it, n))      # pre-populate with the first n elements
    if not result:
        return result
    heapify(result)                   # arrange them into a minheap
    for elem in it:                  
        if element > result[0]:       # new elem is bigger than the smallest-of-the-large
            heapreplace(result, elem) # replace top element with new element
    result.sort()                     # sort the top ten
    return result                     

【讨论】:

【参考方案4】:

是的。创建两个大小为k (k=10) 的堆,一个将less 作为比较器,第二个使用more。其中两个有两个存储“top k”元素的结构。

遍历每个元素并放入每个堆中。如果元素出堆,忘记它们,这意味着它们不在前 10 中。

我相信这是一种称为 Hadian-Sobel 算法的变体。它是堆排序的基础。有点像用于快速排序的分区(我相信 Hoare 算法)。顺便说一句,这里也可以使用。

这样你得到O(n) * 2 O(log k)N 元素乘以 heap_insert 的大小为 k。这是O(n log k),对于k=10,基本上是线性的。

【讨论】:

【参考方案5】:

您可能想太多了,您只需要扫描一次数组并将其填充到两个数组中,通过比较最大最小值和最小最大值来跟踪 10 个最小值和 10 个最大值。 O(n)

一个排序有 O(n log n)

【讨论】:

以上是关于给定 n 个数字的最小和最大 10 个数字的主要内容,如果未能解决你的问题,请参考以下文章

求最大最小数

1483.求最大最小数

最大数

如何在 Pig 和 Hive 中找到第 n 个最大和最小的数字?

2017.11.14

JAVA怎样随机生成10W个数字, 要求: 10W个数字总等于50W而且每个数字最小1最大100, 求代码及思路