剑指offer_29:最小的k个数
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
示例 1:
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:
输入:arr = [0,1,2,1], k = 1
输出:[0]
限制:
0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000
1、直接排序
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
int[] res=new int[k];
Arrays.sort(arr);
for(int i=0;i<k;i++){
res[i]=arr[i];
}
return res;
}
}
2、快速选择(快排思想)
快排就是在把元素分为两类,一边比基准小,一边比基准大,这道题只需要选择一侧的数据就行
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if(k==0){
return new int[0];
}else if(arr.length<=k){
return arr;
}
part(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 part(int[] a,int left,int right,int k){
int num=quickSort(a,left,right);//获取分界值
if(num==k){
return;
}else if(num>k){//缩小范围
part(a,left,num-1,k);
}else{
part(a,num+1,right,k);
}
}
public int quickSort(int[] a,int left,int right){
int i=left;
int j=right+1;
int v=a[left];//a[left]作为分界点
while(true){
while(a[++i]<v){//向右找比分界点大的值
if(i==right){//直到范围的最右边
break;
}
}
while(a[--j]>v){//向左找比分界点小的值
if(j==left){//直到范围的最左边
break;
}
}
if(i>=j){//相遇退出
break;
}
swap(a,i,j);//未相遇,交换
}
swap(a,left,j);//将分界点放在正确的位置,只能用j,不能用i。例子:0 1 2 1
return j;
}
public void swap(int[] a,int x,int y){
int t=a[x];
a[x]=a[y];
a[y]=t;
}
}
3、堆排序
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if(k==0){
return new int[0];
}else if(arr.length<=k){
return arr;
}
Queue<Integer> heap=new PriorityQueue<>((v1,v2)->v2-v1);
for(int i:arr){
if(heap.size()<k){
heap.offer(i);
}else if(heap.peek()>i){
heap.poll();
heap.offer(i);
}
}
int[] res=new int[heap.size()];
int index=0;
for(int i:heap){
res[index++]=i;
}
return res;
}
}