题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
思路:如果不考虑时间效率的话,很容易想到就是把数组排序,直接用Arrays.sort(int[] a)方法可以对指定的 int 型数组按数字升序进行排序(但有一点注意,这样就会修改输入的数组)。该排序算法是一个经过调优的快速排序法,此算法在许多数据集上提供 n*log(n) 性能。排序之后位于前k个数就是最小的k个数。
Note:
如果不想修改数组,可以将数组中的值放进ArrayList中,然后用Collections.sort(List<T> list)
该排序算法是一个经过修改的合并排序算法(其中,如果低子列表中的最高元素小于高子列表中的最低元素,则忽略合并)。此算法提供可保证的 n log(n) 性能。 此实现将指定列表转储到一个数组中,并对数组进行排序,在重置数组中相应位置处每个元素的列表上进行迭代。这避免了由于试图原地对链接列表进行排序而产生的 n2 log(n) 性能。
代码如下:
import java.util.ArrayList;
import java.util.Collections;
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>input.length||k<=0)
return list;
//不改变原来的数组,可以把数组值放进list中
for(int i=0;i<input.length;i++){
list.add(input[i]);
}
//排序
Collections.sort(list);
//返回前k个数,即移除后面的数
while(list.size()>k){
list.remove(k);
}
return list;
}
public static void main(String[] args){
int[] input={4,5,1,6,2,7,3,8};
System.out.println(GetLeastNumbers_Solution(input,4).toString());
}
}
其他解法:基于Partition函数的O(n)解法
题目中只说找出最小的k个数,没说k的数必须有序,所以基于Partition函数的思想,可以令比第k个数字小的在左边,比其大的在右边,这样返回左面的k个数,即可。
代码如下:
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>input.length||k<=0)
return list;
int start=0;
int end=input.length-1;
int index=RandomizedPartition(input,start,end);
//第k个数,下标为k-1,使得input[0..k-2]<=input[k-1]<input[k..end-1]
while(index!=k-1){
if(index>k-1)
index=RandomizedPartition(input,start,index-1);
else
index=RandomizedPartition(input,index+1,end);
}
//把前k个数放进list中
for(int i=0;i<k;i++){
list.add(input[i]);
}
return list;
}
public static int RandomizedPartition(int[] input, int start, int end) {
int temp=(int)(Math.random()*(end - start + 1)) + start;
swap(input,temp,end);
int i=start-1;
for(int j=start;j<end;j++){
if(input[j]<=input[end]){
i++;
swap(input,i,j);
}
}
swap(input,i+1,end);
return i+1;
}
public static void swap(int[] array, int k, int end) {
int temp=array[k];
array[k]=array[end];
array[end]=temp;
}
public static void main(String[] args){
int[] input={4,5,1,6,2,7,3,8};
System.out.println(GetLeastNumbers_Solution(input,4).toString());
}
}
如果不能修改输入数组,怎么办?
如果不修改输入数组,且又把前k个数保存,只能创建一个k大小的容器用来存放最小的k个数。然后怎么做?
当容器不满时(数字小于k),直接放进容器里。
当容器满时,不能直接放进去,想到可以利用操作系统中页面置换的思想,把已在容器中的不满足最小k个数的数字替换出去,首先替换出去的应该是k个数中最大的(可以排除),当然如果待插入的数字比容器中最大的数字还大,无需替换,直接遍历下一个。
总之,当容器满时需要做3件事:1容器中找到最大数;2有可能删除最大数;3在2的基础上向容器中加入一个新的数。想到最大堆满足在O(1)时间找到最大数,且在O(logk)时间删除和插入。