按顺序查找数组中k个最大元素的最快方法是什么(即从最大元素到第k个最大元素)?
答案 0 :(得分:11)
一个选项如下:
使用线性时间selection algorithm,如中位数或中位数,找到第k个最大元素并重新排列元素,使第k个元素前向的所有元素都大于第k个元素。
使用快速排序算法(如heapsort或quicksort)对第k个前进的所有元素进行排序。
步骤(1)花费时间O(n),步骤(2)花费时间O(k log k)。总的来说,算法在时间O(n + k log k)内运行,这非常非常快。
希望这有帮助!
答案 1 :(得分:1)
C ++还提供了partial_sort算法,它解决了选择最小k个元素(已排序)的问题,时间复杂度为O(n log k)。没有提供用于选择最大k个元素的算法,因为这应该通过反转排序谓词来完成。
对于Perl,CPAN提供的模块Sort :: Key :: Top提供了一组函数,用于使用多个排序和自定义键提取过程从列表中选择前n个元素。此外,Statistics :: CaseResampling模块提供了使用quickselect计算分位数的功能。
Python的标准库(自2.4起)包括heapq.nsmallest()和nlargest(),返回排序列表,前者在O(n + k log n)时间内,后者在O(n log k)时间内。 / p>
答案 2 :(得分:1)
基数排序解决方案:
时间复杂度:O(N * L),其中L =最大元素的长度,可以假设L = O(1)。 使用的空间:O(N)用于基数排序。
然而,我认为基数排序具有高昂的开销,使其线性时间复杂度降低了吸引力。
答案 3 :(得分:1)
1)在O(n)中构建一个最大堆树 2)使用Extract Max k次从最大堆O(klogn)获得k个最大元素
时间复杂度:O(n + klogn)
使用STL的C ++实现如下:
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int main() {
int arr[] = {4,3,7,12,23,1,8,5,9,2};
//Lets extract 3 maximum elements
int k = 3;
//First convert the array to a vector to use STL
vector<int> vec;
for(int i=0;i<10;i++){
vec.push_back(arr[i]);
}
//Build heap in O(n)
make_heap(vec.begin(), vec.end());
//Extract max k times
for(int i=0;i<k;i++){
cout<<vec.front()<<" ";
pop_heap(vec.begin(),vec.end());
vec.pop_back();
}
return 0;
}
答案 4 :(得分:0)
@ templatetypedef的解决方案可能是最快的解决方案,假设您可以修改或复制输入。
或者,您可以使用堆或BST(C ++中的set
)在给定时刻存储k个最大元素,然后逐个读取数组元素。虽然这是O(n lg k),但它不会修改输入,只使用O(k)附加内存。它也适用于流(当你不知道从头开始的所有数据时)。
答案 5 :(得分:0)
这是一个O(N + k lg k)
复杂度的解决方案。
int[] kLargest_Dremio(int[] A, int k) {
int[] result = new int[k];
shouldGetIndex = true;
int q = AreIndicesValid(0, A.Length - 1) ? RandomizedSelet(0, A.Length-1,
A.Length-k+1) : -1;
Array.Copy(A, q, result, 0, k);
Array.Sort(result, (a, b) => { return a>b; });
return result;
}
AreIndicesValid
和RandomizedSelet
在this github source file中定义。
答案 6 :(得分:0)
有关性能和受限资源的问题。
为前3个值创建一个值类。使用这种累加器减少并行流。根据上下文(内存,电源)限制并行度。
class BronzeSilverGold {
int[] values = new int[] {Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE};
// For reduction
void add(int x) {
...
}
// For combining two results of two threads.
void merge(BronzeSilverGold other) {
...
}
}
必须在您的星座图中限制并行性,因此请在以下位置指定N_THREADS:
try {
ForkJoinPool threadPool = new ForkJoinPool(N_THREADS);
threadPool.submit(() -> {
BronzeSilverGold result = IntStream.of(...).parallel().collect(
BronzeSilverGold::new,
(bsg, n) -> BronzeSilverGold::add,
(bsg1, bsg2) -> bsg1.merge(bsg2));
...
});
} catch (InterruptedException | ExecutionException e) {
prrtl();
}