剑指Offer 40.最小的k个数
分析
- 方法一:堆
- 用大顶堆来做,用java自带的 PriorityQueue优先队列,由于默认是小顶堆,重写比较器改为大顶堆,队列最前端存储的就是队列中最大的元素
- 每次从堆顶弹出来的元素都是堆中最大的,最小的 k 个元素一定会留在堆里。这样,把数组中的元素全部入堆之后,堆中剩下的 k 个元素就是最大的 k 个数了。
//方法一:堆
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if(k == 0) return new int[0];
Queue<Integer> heap = new PriorityQueue<>((i1, i2) -> i2 - i1);
for(int i = 0; i < arr.length; i++) {
if(heap.size() < k || arr[i] < heap.peek()) { //队列元素小于k或者要加入的元素小于队列首元素,则将当前元素加入队列中
heap.offer(arr[i]);
}
if(heap.size() > k) { //队列长度大于k,则将队首元素取出,保证队列中其他元素都小于队首元素
heap.poll();
}
}
int[] res = new int[k];
for (int i = 0; i < k; i++) {
res[i] = heap.poll();
}
return res;
}
}
-
方法二:快速选择
-
从数组中随机选取一个枢纽元素 v,然后原地移动数组中的元素,使得比 v 小的元素在 v 的左边,比 v 大的元素在 v 的右边
-
我们的目的是寻找最小的 k 个数。假设经过一次 partition 操作,枢纽元素位于下标 m,也就是说,左侧的数组有 m 个元素,是原数组中最小的 m 个数。那么:
-
若 k = m,我们就找到了最小的 k 个数,就是左侧的数组;
-
若 k<m ,则最小的k个数一定都在左侧数组中,我们只需要对左侧数组递归地 parition 即可;
-
若 k>m,则左侧数组中的 m 个数都属于最小的 k 个数,我们还需要在右侧数组中寻找最小的 k−m 个数,对右侧数组递归地 partition 即可。
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if(k == 0) return new int[0];
//划分数组
partitionArray(arr, 0, arr.length - 1, k);
int[] res = new int[k];
for (int i = 0; i < k; i++) {
res[i] = arr[i];
}
return res;
}
public void partitionArray(int[] arr, int left, int right, int k) {
int m = partition(arr, left, right); //做快速查找操作
if(k == m) { //此时数组前m个数,就是最小的m个数
return;
}else if(k < m) { //最小的k个数一定在前m个数中,递归划分
partitionArray(arr, left, m - 1, k);
}else { //在右侧数组中寻找最小的k-m个数
partitionArray(arr, m + 1, right, k);
}
}
public partition(int[] arr, int left, int right) {
int i = left;
int j = right + 1;
int num = arr[left]; //此为枢纽元素,小于此数的放在左边,大于此数的放在右边
while(true) {
while(arr[++i] < num) { //找到比num大的数
if(i == right) break;
}
while(arr[--j] < num) { //找到比num小的数
if(j == left) break;
}
if(i >= j) break; //如果此时i>=j,说明两头遍历到中间都符合要求
swap(arr, i, j); //交换两者的位置
}
swap(arr, left, j); //把枢纽元素放到中间,这样在该索引左边的就都是小于枢纽元素,右边都是大于枢纽元素
return j; //返回枢纽元素的索引值,用于判断小于枢纽元素的值有几个
}
public void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}