最简单(虽然不是最有效)的方法是对包含最后 4 个元素的子数组进行排序。
您可以使用Arrays.sort()
排序和Arrays.copyOfRange()
获取子数组。
int[] arr = new int[] {1232, -1221, 0, 345, 78, 99};
Arrays.sort(arr);
int[] top4 = Arrays.copyOfRange(arr, arr.length-4,arr.length);
System.out.println(Arrays.toString(top4));
为了更有效的解决方案,可以维护前 K个元素的最小堆或使用选择算法找到前 4 个元素。此线程中描述了这两种方法。
尽管选择算法提供了O(n)
解决方案,但最小堆解决方案(即O(nlogK)
)应该有更好的常数,尤其是对于小k
的可能更快。
PS(编辑):
对于 4 个元素,您可能会发现调用循环 4 次,并在每个元素中找到一个最大值(并在每次迭代中将旧的最大值更改为 -infinity)将比更“复杂”的方法更有效,因为它需要顺序读取并且具有相当小的常量。对于 large ,这当然不是真的,而是k
衰减到O(n^2)
fork->n
EDIT2:基准测试:
为了好玩,我对附加的代码进行了基准测试。结果是:
[naive, sort, heap] = [9032, 214902, 7531]
我们可以看到,naive 和 heap 比基于排序的方法要好得多,并且 naive 比基于堆的方法稍慢。我做了一个wilcoxon 测试来检查 naive 和 heap 之间的差异是否具有统计显着性,我得到的 P_Value 为3.4573e-17
. 这意味着两种方法“相同”的概率为 3.4573e-17(极小)。由此我们可以得出结论——基于堆的解决方案比单纯的排序解决方案具有更好的性能(我们已经通过经验证明了这一点!)。
附件:我使用的代码:
public static int[] findTopKNaive(int[] arr, int k) {
int[] res = new int[k];
for (int j = 0; j < k; j++) {
int max=Integer.MIN_VALUE, maxIdx = -1;
for (int i = 0; i < arr.length; i++) {
if (max < arr[i]) {
max = arr[i];
maxIdx = i;
}
}
arr[maxIdx] = Integer.MIN_VALUE;
res[k-1-j] = max;
}
return res;
}
public static int[] findTopKSort(int[] arr, int k) {
Arrays.sort(arr);
return Arrays.copyOfRange(arr, arr.length-k,arr.length);
}
public static int[] findTopKHeap(int[] arr, int k) {
PriorityQueue<Integer> pq = new PriorityQueue<Integer>();
for (int x : arr) {
if (pq.size() < k) pq.add(x);
else if (pq.peek() < x) {
pq.poll();
pq.add(x);
}
}
int[] res = new int[k];
for (int i =0; i < k; i++) res[i] = pq.poll();
return res;
}
public static int[] createRandomArray(int n, Random r) {
int[] arr = new int[n];
for (int i = 0; i < n; i++) arr[i] = r.nextInt();
return arr;
}
public static void main(String... args) throws Exception {
Random r = new Random(1);
int k = 4;
int repeats = 200;
int n = 5000000;
long[][] results = new long[3][repeats];
for (int i = 0; i < repeats; i++) {
int[] arr = createRandomArray(n, r);
int[] myCopy;
myCopy = Arrays.copyOf(arr, n);
long start = System.currentTimeMillis();
findTopKNaive(myCopy, k);
results[0][i] = System.currentTimeMillis() - start;
myCopy = Arrays.copyOf(arr, n);
start = System.currentTimeMillis();
findTopKSort(myCopy, k);
results[1][i] = System.currentTimeMillis() - start;
myCopy = Arrays.copyOf(arr, n);
start = System.currentTimeMillis();
findTopKHeap(myCopy, k);
results[2][i] = System.currentTimeMillis() - start;
}
long[] sums = new long[3];
for (int i = 0; i < repeats; i++)
for (int j = 0; j < 3; j++)
sums[j] += results[j][i];
System.out.println(Arrays.toString(sums));
System.out.println("results for statistic test:");
for (int i = 0; i < repeats; i++) {
System.out.println(results[0][i] + " " + results[2][i]);
}
}