题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
输出最小的k 个数不一定要求有序。
题解:
方法一:
把输入的 n 个数排序,排序后位于最前面的 k 个数就是最小的 k 个数。
时间复杂度为 O(nlogn)
方法二:
利用快排的思想,通过多轮快排,数组下标从0开始,确定出第 k-1 个位置上的数,则位于它左边的数都比他小,位于它右边的数都比他大。则0到 k-1 位置上的数字就是最小的k 个数,不保证有序。
时间复杂度 O(n)
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
ArrayList<Integer> result=new ArrayList<>();
if (input == null || input.length == 0 || k <= 0 || k > input.length ) {
return result;
}
int x=input[k-1];
int start=0;
int end=input.length-1;
int index=quickGetPosition(input,start,end);//确定一趟快排后,哪个位置的元素确定了
while(index!=k-1){// 确定的不是位置 k-1 上的元素
if(index>k-1){//如果index在k-1 的右边,则从index左边继续找
end=index-1;
index=quickGetPosition(input, start, end);
}else{//如果index在k-1 的左边,则从index右边继续找
start=index+1;
index=quickGetPosition(input, start, end);
}
}
for(int i=0;i<k;i++){// 将0 到k-1 位置上的数字将入result
result.add(input[i]);
}
return result;
}
private int quickGetPosition(int[] array, int start, int end) {
if (start < end) {
int x = array[start];
while (start < end) {
while (start < end && array[end] > x) {
end--;
}
if (start < end) {
array[start] = array[end];
start++;
}
while (start < end && array[start] < x) {
start++;
}
if (start < end) {
array[end] = array[start];
end--;
}
}
array[start] = x;
}
return start;
}
}
方法三:
用最大堆,设置容量为k, 如果最大堆已有元素小于 k 个,直接加入,如果等于k 个,则比较堆顶元素,是否大于 当前遍历元素,如果大于,则弹出堆顶元素,将改元素加入最大堆 。最后最大堆中的元素就是数组中最小的 k 个元素。
对最大堆的插入和删除元素,时间复杂度为 O(logk),获得堆顶元素时间复杂度为O(1)
对于n个元素的数组,总的时间复杂度为 O(nlogk)
import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Comparator;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
ArrayList<Integer> result = new ArrayList<>();
if (input == null || input.length == 0 || k <= 0 || k > input.length) {
return result;
}
Comparator<Integer> comparator = new Comparator<Integer>() {// 降序
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
};
Queue<Integer> queue = new PriorityQueue<Integer>(k, comparator);// 最大堆
for (int i = 0; i < input.length; i++) {
if (queue.size() < k) {
queue.add(input[i]);
} else {
if(queue.peek()>input[i]){
queue.poll();
queue.add(input[i]);
}
}
}
while(!queue.isEmpty()){
result.add(queue.poll());
}
return result;
}
}
两种算法比较:
~ | 基于quickGetPosition函数 | 基于最大堆 |
---|---|---|
时间复杂度 | O(n) | O (nlogk) |
是否需要修改输入数组 | 是 | 否 |
是否适用于海量数据 | 否 | 是 |
方法三虽然时间复杂度大了点,但是不用修改输入数组,且适合用于海量数据。
对于n 很大的海量数据,不能一次性载入内存,我们可以从硬盘中一次读入一个数字,判断是否放入 最大堆中,内存只要能容纳最大堆即可,因此方法三适用于n 很大并且k 较小的情况。