面试题40:最小的k个数
题目描述
输入n个整数,找出其中最小的k个数。例如输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
问题分析
最容易想到的方法就是排序,取相应数字的元素即可。我这里为了方便直接使用了快排来做这个。
想拉开与别人的差距,难免需要优化时间效率,我们可以这样做:依次遍历n个整数,用一个容器存放最小的k个数字,每遇到比容器中最大的数字还小的数字时,将最大值替换为该数字。容器可以使用最大堆或者红黑树来实现。堆相比红黑树更容易实现,我们这里采用堆。
问题解答
思路一(不推荐)
public ArrayList<Integer> GetLeastNumbers(int [] input, int k) {
ArrayList<Integer> leastNumbers = new ArrayList<>();
if(input==null || k<=0 || k>input.length) {
return leastNumbers;
}
int start=0;
int end=input.length-1;
int index=partition(input,start,end);
while(index!= k-1){
if(index< k-1){
start=index+1;
index=partition(input,start,end);
}else{
end=index-1;
index=partition(input,start,end);
}
}
for(int i=0;i<k;i++){
leastNumbers.add(input[i]);
}
return leastNumbers;
}
private int partition(int[] arr, int start,int end){
int pivotKey=arr[start];
while(start<end){
while(start<end && arr[end]>=pivotKey) {
end--;
}
swap(arr,start,end);
while(start<end && arr[start]<=pivotKey){
start++;
}
swap(arr,start,end);
}
return start;
}
private void swap(int[] arr, int i,int j){
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
思路二(推荐)
public ArrayList<Integer> GetLeastNumbers(int [] input, int k) {
ArrayList<Integer> leastNumbers = new ArrayList<>();
if(input==null || k<=0 || k>input.length) {
return leastNumbers;
}
int[] numbers=new int[k]; //用于放最小的k个数
//先放入前k个数
System.arraycopy(input, 0, numbers, 0, k);
for(int i = k/2-1; i>=0; i--){
adjustHeap(numbers,i,k-1);//将数组构造成最大堆形式
}
for(int i = k; i<input.length; i++){
if(input[i]<numbers[0]){ //存在更小的数字时
numbers[0]=input[i];
adjustHeap(numbers,0,k-1);//重新调整最大堆
}
}
for(int n:numbers)
leastNumbers.add(n);
return leastNumbers;
}
//最大堆的调整方法
private void adjustHeap(int[] arr,int start,int end){
int temp=arr[start];
int child=start*2+1;
while(child<=end){
if(child+1<=end && arr[child+1]>arr[child]) {
child++;
}
if(arr[child]<temp) {
break;
}
arr[start]=arr[child];
start=child;
child=child*2+1;
}
arr[start]=temp;
}