TopK问题的描述:
指定n个数字,找出其中最大的k个数,这就是经典的TopK问题
解决方法一:全局排序
- 将n个数进行全排序,取出最大的k个,即是所需的结果
- 代码:
public int[] topK(int[] array, int k) {
Arrays.sort(array);
return Arrays.copyOfRange(array, array.length - k, array.length);
}
- 时间复杂度是O(N*logN)
解决方法二:局部排序
解决方法三:堆
解决方法四:随机选择
- 使用减治的的思想,制定一个元素flag将比flag大的元素放在他左边,比他小的放在他右边
- 如果flag的的下标index比k大说明前k个大的元素都在flag左边的区间,然后在他左区间内重复第一步直到找到下标为k的值
- 如果flag的下标index比k小说明只要在他的右区间内重复第一步找到下标为k-index的值
- 找到第k个大的值后再进行此一步骤,它左边的所有的元素就是前k个最大的值
代码:
public int[] topK5(int[] array, int k) {
int left = 0;
int right = array.length - 1;
//因为数组下标是以0开始的,因此第k个的小标为k - 1,因此传入的为k - 1
int flag = RS(array, left, right, k - 1);
//返回值flag为第k个最大值的下标,因此需要前k个最大的值时,拷贝数组的范围是[0, flag + 1)
return Arrays.copyOfRange(array, 0, flag + 1));
}
private int RS(int[] array, int left, int right, int k) {
if (left >= right) {
return left;
}
int index = partition(array, left, right);
int temp = index - left;
if(temp >= k) {
return RS(array, left, index - 1, k);
} else {
return RS(array, index + 1, right, k - index);
}
}
private int partition(int[] array, int left, int right) {
int tmp = array[left];
int l = left;
int r = right;
while(l < r) {
while(l < r && array[r] <= tmp) {
r--;
}
array[l] = array[r];
while(l < r && array[l] >= tmp) {
l++;
}
array[r] = array[l];
}
array[l] = tmp;
return l;
}
- 时间复杂度:O(N)