解法来自于:小美算法 剑指Offer 40题 最小的k个数 java版本 层层深入的三种解法来赢得面试
解法一:排序+取前k个数
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;
}
}
解法二:优先队列大顶堆解法
class Solution {
//利用小顶堆 大根堆的做法
public int[] getLeastNumbers(int[] arr, int k) {
//创建一个队列(默认是最大堆(默认每次被踢出的都是最小的),,我们需要把它改成最小堆(每次被提出的都是最大的))
Queue<Integer> queue=new PriorityQueue<>((o1,o2)->(o2.compareTo(o1)));
for(int i:arr){
queue.add(i);
//保证队列里只装k个数,且不断保留最小的k个
while(!queue.isEmpty()&& queue.size()>k) queue.poll();
}
//创建新的数组,返回结果
int[] res=new int[k];
for(int i=0;i<k;i++){
res[i]=queue.poll();
}
return res;
}
}
补充:大顶堆的建立过程:
解法三:快排+二分
class Solution {
//快排+二分
public int[] getLeastNumbers(int[] arr, int k) {
//排除空数组情况
if(arr==null||arr.length==0) return new int[0];
//记录数组两端,不断缩小范围
int lo=0,hi=arr.length-1;
//使用二分法,不断筛选范围
while(lo<hi){
//partition是通过不断交换,将区域内某一个随机元素为界划分大小
int index=partition(arr,lo,hi);
//二分的结束条件,如果随机数索引到末尾,退出循环
if(index== k-1)break;
//随机数在k的范围之内,就向后移动
else if(index < k-1) lo=index+1;
//随机数在k的范围之外就向前移动
else hi=index-1;
}
return Arrays.copyOfRange(arr,0,k);
}
/*
partition函数原理: 保证最后返回的随机数左面小于随机数,右面大于随机数
使用pivot标记第一个元素,
遍历区域里的元素,
如果遇到当前元素比第一个元素小的时候,若index不和i相等则交换++index的元素和当前元素的位置
*/
public int partition(int[] arr,int lo,int hi){
//用pivot记录首个元素,他就是我们最后该返回的元素
int pivot=arr[lo];
//用index记录当前lo下标
int index=lo;
//遍历所有区域内的元素
for(int i=lo;i<=hi;i++){
//如果当前元素小于首元素,交换位置
if(arr[i]<pivot) swap(arr,i,++index);
}
//将第一个元素和最后结束的数交换
swap(arr,lo,index);
return index;
}
public void swap(int[] arr,int i,int j){
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
解法三图示理解: