题目描述
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
限制:
0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000
输入输出样例
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
题解
0 最好的方法就是快速选择
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if(k == 0 || k > arr.length) return new int[0];
// if(k == arr.length) return arr;
// 使用快速选择的方法
quickSearch(arr, 0, arr.length - 1, k-1);
int[] ret = new int[k];
for(int i=0; i<k; i++){
ret[i] = arr[i];
}
return ret;
}
// 快速选择
public void quickSearch(int[] arr, int left, int right, int k){
int index = partition(arr, left, right);
// 返回的时候,下标是k-1,那么之前的 0-(k-2) 即k-1个元素,下标k-1处也是有序的
if(index == k){
return;
}
else if(index > k){
// 当前下标大于index了,需要在左边继续切分
quickSearch(arr, left, index-1, k);
}
else if(index < k){
quickSearch(arr, index+1, right, k);
}
}
// 区间切分
public int partition(int[] arr, int left, int right){
int
l = left,
r = right+1,
pivot = arr[left];
while(true){
// 关键步骤
while(++l <= right && arr[l] < pivot);
while(left <= --r && arr[r] > pivot);
if(l >= r) break;
swap(arr, l, r);
}
swap(arr, left, r);
return r;
}
// 数组元素交换
public void swap(int[] arr, int id1, int id2){
int temp = arr[id1];
arr[id1] = arr[id2];
arr[id2] = temp;
}
}
1 可以利用最大堆的特性,自动维护。
代码:
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if(k == 0 || arr.length == 0) return new int[0];
// 默认是小根堆,实现大根堆需要重写一下比较器
Queue<Integer> pq = new PriorityQueue<>((v1, v2) -> v2 - v1);
for(int num:arr){
if(pq.size() < k){
pq.offer(num);
}else if(num < pq.peek()){
pq.poll();
pq.offer(num);
}
}
int[] ans = new int[pq.size()];
int idx = 0;
for(int num:pq){
ans[idx++] = num;
}
return ans;
}
}
2 可以将数组使用快速排序排序好,然后抽取前面的k个即可
代码:
直接调用工具类的排序函数
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if(k > arr.length) return new int[0];
int[] ret = new int[k];
// 数组——快速排序
Arrays.sort(arr);
// 输出前k个数字
for(int i=0; i<k; i++){
ret[i] = arr[i];
}
return ret;
}
}
3 使用计数排序(桶排序)
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if (k == 0 || arr.length == 0) {
return new int[0];
}
// 统计每个数字出现的次数
int[] counter = new int[10001];
for (int num: arr) {
counter[num]++;
}
// 根据counter数组从头找出k个数作为返回结果
int[] res = new int[k];
int idx = 0;
for (int num = 0; num < counter.length; num++) {
while (counter[num]-- > 0 && idx < k) {
res[idx++] = num;
}
if (idx == k) {
break;
}
}
return res;
}
}