题目:输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
思路一:利用快速排序的parition函数。如果基于数组的第K个数字来调整,则使得比第K个数字小的所有数字都位于数组的左边,比第K个数字大的所有数字都位于数组的右边,调整之后,位于数组中左边的K个数字就是最小的K个数字(这K个数字不一定是排序的,这只是快速排序的第一趟排序)。时间复杂度为O(n)
注意点:考虑数组为空的情况,以及K<0或者K超出数组长度的情况;
代码:
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> list=new ArrayList<Integer>();
if(input==null||input.length==0||k<=0||k>input.length){
return list;
}
int start=0;
int end=input.length-1;
int index=partition(input,start,end);
while (index!=k-1){
if(index>k-1){
end=index-1;
index=partition(input,start,end);
}else{
start=index+1;
index=partition(input,start,end);
}
}
for(int i=0;i<k;i++){
list.add(input[i]);
}
return list;
}
public int partition(int[] arr, int startIndex, int endIndex) {
// 取第一个位置的元素作为基准元素
int pivot = arr[startIndex];
int left = startIndex;
int right = endIndex;
while( left != right) {
//控制right指针比较并左移
while(left<right && arr[right] > pivot){
right--;
}
//控制right指针比较并右移
while( left<right && arr[left] <= pivot) {
left++;
}
//交换left和right指向的元素
if(left<right) {
int p = arr[left];
arr[left] = arr[right];
arr[right] = p;
}
}
//pivot和指针重合点交换
arr[startIndex] =arr[left];
arr[left] = pivot;
return left;
}
}
思路二:利用大顶堆。新创建一个大小为K的容器,对该容器进行操作,适合海量数据处理;
利用大顶堆来存储最小的K个数字,如果新遍历的数字比堆顶元素小,则删除堆顶元素,添加遍历到的元素。
堆排序思想可参考:堆排序算法 https://blog.csdn.net/AnNing_007/article/details/99639127
代码:
import java.util.ArrayList;
public class Solution {
public static ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> list=new ArrayList<Integer>();
if(input==null||input.length==0||k<=0||input.length<k){
return list;
}
int [] newArray=new int[k];
for(int i=0;i<k;i++){
newArray[i]=input[i];
}
//1.构建大顶堆
for(int i=k/2-1;i>=0;i--){
//从第一个非叶子结点从下至上,从右至左调整结构
adjustHeap(newArray,i,k);
}
for(int i=k;i<input.length;i++){
if(input[i]<newArray[0]){
swap(newArray,0,k-1);
newArray[k-1]=input[i];
//for(int j=k/2-1;j>=0;j--)
//从第一个非叶子结点从下至上,从右至左调整结构
adjustHeap(newArray,0,k);
}
}
for(int i=0;i<k;i++){
list.add(newArray[i]);
}
return list;
}
/**
* 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
* @param arr
* @param i 第一个非叶子节点
* @param length 数组的长度
*/
public static void adjustHeap(int []arr,int i,int length){
int temp = arr[i];//先取出当前元素i
for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点
k++;
}
if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
arr[i] = arr[k];
i = k;
}
}
arr[i] = temp;//将temp值放到最终的位置
}
//交换
public static void swap(int[] arr, int n, int m) {
int temp = arr[n];
arr[n] = arr[m];
arr[m] = temp;
}
}