我刚刚开始使用算法和排序,所以请耐心等待......
我们说我有一个50000整数的数组。
我需要选择最小的30000个
我想到了两种方法:
1.迭代整个数组并找到每个最小的整数
2.我首先对整个阵列进行排序,然后只选择第一个30000.
任何人都能告诉我它的区别是什么,哪种方法会更快,为什么? 如果阵列更小或更大怎么办?答案会改变吗?
答案 0 :(得分:2)
选项1听起来像天真的解决方案。它将涉及通过数组来找到30000次最小的项目。每次找到最小的时候,可能会将该项交换到数组的开头或结尾。在基本术语中,这是 O(n ^ 2)复杂度。
所涉及的实际操作数量将小于 n ^ 2 ,因为 n 每次都会减少。所以你会有大约50000 + 49999 + 49998 + ... + 20001,这相当于超过10亿次(1000万次)迭代。
选项2将使用类似快速排序或类似的算法,通常 O(n.logn)。
这里提供实际数据更难,因为一些有效的排序算法可能会出现 O(n ^ 2)的最坏情况。但是,假设您使用的是一个保证良好的 O(n.logn)。这将达到50000 * 15.61,约为78万。
所以很明显,在这种情况下,选项2获胜。
如果阵列更小或更大怎么办?答案会改变吗?
除非数组变得非常小,否则答案仍然是选项2.数组越大,选项2就越有利。这是时间复杂性的本质。 O(n ^ 2)的增长速度远远超过 O(n.logn)。
一个更好的问题是“如果我想要更少的最小值,那么选项1何时变得更可取?”。虽然答案稍微复杂一些,因为有很多因素(比如选项1和选项2中的“一个操作”,以及内存访问模式等等其他问题),你可以得到简单的答案直接来自时间复杂性。当要选择的最小值的数量低于 n.logn 时,选项1将变得更可取。对于50000元素数组,这意味着如果要选择15个或更少的最小元素,则选项1将获胜。
现在,考虑一个选项3,将数组转换为 min-heap 。构建堆是 O(n),从中删除一个项目是 O(logn)。您将删除30000项。所以你有建造成本加上搬迁费用:50000 + 30000 * 15.6 =约52万。这忽略了每次删除元素时 n 变小的事实。它仍然是 O(n.logn),就像选项2一样,但它可能更快:你没有费心去排序你不关心的元素,从而节省了时间。
我应该提到在所有三种情况下,结果将是按排序顺序的最小30000值。可能还有其他解决方案可以按特定顺序为您提供这些值。
答案 1 :(得分:2)
30k接近50k。只需对数组进行排序并获得最小的30k,例如Python:sorted(a)[:30000]
。这是O(n * log n)
操作。
如果您需要找到100个最小的项目(100 << 50k
),那么heap可能更合适,例如,在Python中:heapq.nsmallest(100, a)
。它是O(n * log k)
。
如果整数范围有限,您可以考虑O(n)
排序方法,例如counting sort and radix sort。
这里简单的迭代方法是O(n**2)
(二次)。即使是一个约为一百万的温和n
;对于线性算法,它导致~10**12
操作比~10**6
差得多。
答案 2 :(得分:2)
对于几乎所有实际目的而言,排序和获取前30,000可能是最好的。在大多数语言中,这是一行或两行代码。很难出错。
如果你有一个真正苛刻的应用程序,或者只是想摆弄,你可以使用selection algorithm找到第30,000个最大的数字。然后再通过数组将找到29,999个不大的。
有几种众所周知的选择算法只需要O(n)比较,而某些算法对于具有特定属性的数据则是亚线性的。
实践中最快的是QuickSelect,顾名思义,它的工作方式大致类似于部分QuickSort。不幸的是,如果数据的排序非常糟糕,QuickSelect可能需要O(n ^ 2)时间(就像QuickSort一样)。选择枢轴有各种技巧,使得几乎不可能获得最坏情况下的运行时间。
QuickSelect将以重新排序的数组结束,因此最小的30,000个元素位于第一部分(未排序),其次是其余部分。
由于标准选择算法是基于比较的,因此它们可以处理任何类型的可比数据,而不仅仅是整数。
答案 3 :(得分:0)
如果您的输入是整数,则可以使用基数排序或计数排序在潜在的O(N)时间内执行此操作。
另一种方法是通过quickselect获取第30000个最大整数,并简单地遍历原始数组。这具有Θ(N)时间复杂度,但在最坏的情况下具有O(N ^ 2)用于快速选择。