题目描述
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
首先想到的是先排序,再输出前k个,但是不管用什么排序,效果都不是很好。
由于只要找到最小的k个数就好,这k个数不需要从小到大排序,所有快排不需要将所有数字都排序。因为我们是要找下标为k的元素,第一次切分的时候需要遍历整个数组 (0 ~ n) 找到了下标是 j 的元素,假如 k 比 j 小的话,那么我们下次切分只要遍历数组 (0~k-1)的元素就行,反之如果 k 比 j 大的话,那下次切分只要遍历数组 (k+1~n) 的元素就行。
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if(k == 0 || arr.length == 0){
return new int[0];
}
return sort(arr,0,arr.length-1,k-1);
}
public int partition(int[] arr,int startIndex,int endIndex){
int pivot = arr[startIndex];
int left = startIndex;
int right = endIndex;
while(left != right){
while(left<right && arr[right]>pivot){
right --;
}
while(left < right && arr[left] <= pivot){
left ++;
}
if(left < right){
int p = arr[left];
arr[left] = arr[right];
arr[right] = p;
}
}
arr[startIndex] = arr[left];
arr[left] = pivot;
return left;
}
public int[] sort(int[] arr,int startIndex,int endIndex,int k ){
int pivotIndex = partition(arr,startIndex,endIndex);
if(pivotIndex == k){
return Arrays.copyOf(arr, pivotIndex+1) ;
}
return pivotIndex > k? sort(arr, startIndex, pivotIndex - 1, k): sort(arr, pivotIndex + 1, endIndex, k);
}
}
计数排序:
数据范围有限的情况下可以使用计数排序
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 a:arr){
counter[a]++;
}
int res[] = new int[k];
int index = 0;
for(int i = 0;i<counter.length;i++){
while(counter[i] > 0 && index <k){
res[index++] = i;
counter[i] --;
}
}
return res;
}
}
PriorityQueue:
本题是求前 K 小,因此用一个容量为 K 的大根堆,每次 poll 出最大的数,那堆中保留的就是前 K 小。
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if(k == 0 || arr.length == 0){
return new int[0];
}
Queue<Integer> q = new PriorityQueue<>((v1,v2)->v2-v1);
for(int num:arr){
if(q.size()<k){
q.offer(num);
}else if(num<q.peek()){
q.poll();
q.offer(num);
}
}
int[] res = new int[q.size()];
int index = 0;
for(int num:q){
res[index++] = num;
}
return res;
}
}