题目描述: 输入N个数,找出其中最小的K个数。例如,输入1,2,3,4,5,6,7,8,求最小的4个数,既输出1,2,3,4。
比较容易想到的的解法:对N个数进行排序,排序之后位于最前面的k个数就是最小的k个数字。
时间复杂度为O(n)的解法:仅适用于我们可以修改输入数组时
- 使用Partition函数(O(n))进行划分,寻找数组中的第k个数字, 这样位于第k个数字左边的数字都比第k个数字小,位于第k个数字右边的数字都比第k个数字大。
代码:
public class 最小的k个数 {
// 使用partition进行求解,这样的话,比第k小的数字小的都位于数组的左边,比第k个数字大的都位于数组的右边
private static void getMinKNumbers(int[] input, int k) throws Exception {
if (input == null || input.length == 0 || k <= 0) {
return;
}
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);// 时间复杂度为O(n)
}
}
for (int i = 0; i < k; i++) {
System.out.println(input[i]);
}
}
private static int Partition(int[] input, int start, int end) throws Exception {
if (input == null || start < 0 || end > input.length) {
throw new Exception("in partition function, input param error.");
}
int partitionIndex = RandomInRange(start, end); // 随机生成partition的 index
int pivort = input[partitionIndex];
swap(input, partitionIndex, end);
int small = start - 1;
for (int i = start; i < end; i++) {
if (input[i] < pivort) {
small++;
if (small != i) {
// 如果两个下标相等的话,就不进行交换
swap(input, small, i);
}
}
}
small++;
swap(input, small, end);
return small;
}
private static void swap(int[] data, int index1, int index2) {
int temp = data[index1];
data[index1] = data[index2];
data[index2] = temp;
}
/*随机生成划分的中心点*/
private static int RandomInRange(int start, int end) {
Random random = new Random();
// nextInt(n)生成的随机数的范围不包括n,所以我们下面加1。
return random.nextInt(end - start + 1) + start;
}
public static void main(String[] args) throws Exception {
getMinKNumbers(new int[]{
4, 5, 1, 6, 2, 7, 3, 8}, 4);
}
}