1.题目
给定一个数组,找出其中最小的K个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。如果K>数组的长度,那么返回一个空的数组。
示例1
输入 [4,5,1,6,2,7,3,8],4
返回值 [1,2,3,4]
2.解决方案
2.1 排序
思路分析
先对输入的n个数从小到大排序,然后输出前k个数。
时空复杂度分析
T(n)=O(nlogn)
S(n)=O(1)
2.2 partition
思路分析
基于快速排序的partition函数来解决这个问题。
定义问题为f(a[],i,j,k),对数组a[]中i到j的数字获取最小的k个数。
step.1
p=partition(a[],i,j)
step.2
如果p-i+1=k,输出a[i~p]
如果p-i+1>k,则应该在a[i~p]范围内找top k,执行f(a,i,p,k)
如果p-i+1<k,则应该在a[p~j]范围内找top [k-(p-i+1)],执行f(a,p+1,j,k-p+i-1)
时空复杂度分析
T(n)=O(n)
S(n)=O(1)
优缺点分析
缺点
需要修改输入的数据
2.3 适合海量数据的解法
思路分析
维护一个存储k个数字的容器,从数据中先顺序读入k个数字到容器中,然后依次读入每一个数字e:
如果e>=容器中最大的数,则e一定不是top k,接着读下一个
如果e<容器中最大的数,则删除容器中最大的数字,插入数字e
这个容器采用最大堆来实现操作效率最高,每次插入的时间复杂度为O(logk);
这个容器采用数组来实现,每次插入的时间复杂度为O(k)。
时空复杂度分析
T(n)=O(nlogk)
S(n)=O(1)
优缺点分析
优点:
1.不修改输入数据
2.适合海量数据的输入
3.代码
public class NC119 {
public static ArrayList<Integer> result=new ArrayList<Integer>();
public static int partition(int [] input,int i,int j){
int h=j;
int l=i;
while(h>l) {
while (input[h] > input[l] && h > l) h--;
if (input[h] <= input[l] && h > l) {
int tmp = input[l];
input[l] = input[h];
input[h] = tmp;
l++;
}
while (input[l] <= input[h] && h > l) l++;
if (input[l] > input[h] && h > l) {
int tmp = input[l];
input[l] = input[h];
input[h] = tmp;
h--;
}
}
return h;
}
public static void f(int [] input,int i,int j,int k) {
if(j-i+1>k) {
int p = partition(input, i, j);
if (p - i + 1 == k) {
for (int q = i; q <= p; q++) {
result.add(input[q]);
}
}else if(p - i + 1 == k+1){
for (int q = i; q < p; q++) {
result.add(input[q]);
}
}
else if (p - i + 1 > k+1) {
f(input, i, p - 1, k);
} else {
for (int q = i; q <= p; q++) {
result.add(input[q]);
}
f(input, p + 1, j, k - p + i - 1);
}
}else if(j-i+1==k){
for(int q=i;q<=j;q++){
result.add(input[q]);
}
}
}
}