题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
解法一:根据快速排序,基于数组的第k个数字来调整,使得k左边的数字都小于k,k右边的数字都大于k。
调整后,左边的k个数字就是最小的k个数字,时间复杂度O(n)。
/**
* @author yuan
* @date 2019/2/19
* @description 基于partition,时间复杂度O(n)
*/
public class 最小的K个数 {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> result = new ArrayList<>(input.length);
if (k < 1 || k > input.length) {
return result;
}
int l = 0, r = input.length - 1;
int index = partition(input, l, r);
while (index != k - 1) {
if (index > k - 1) {
r = index - 1;
index = partition(input, l, r);
} else {
l = index + 1;
index = partition(input, l, r);
}
}
for (int i = 0; i < k; i++) {
result.add(input[i]);
}
return result;
}
/**
* 切片
* @param a
* @param left
* @param right
* @return
*/
private int partition(int[] a, int left, int right) {
int i = left, j = right;
int base = a[left];
while (i < j) {
while (i < j && a[j] >= base) {
--j;
}
while (i < j && a[i] <= base) {
++i;
}
swap(a, i, j);
}
swap(a, left, i);
return i;
}
private void swap(int[] a, int i, int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
解法二:用最大堆,时间复杂度O(nlgk)
先创建一个大小为k的堆,接下来每次读取一个整数,当堆的数字小于k,直接放入堆中。
若堆已经有k个数字,将堆中最大的值和当前整数比较,如果当前整数小于堆的最大值,则将堆中的最大值替换为当前整数。
如果当前整数比堆的最大值大,则不可能是最小的k个整数之一,直接抛弃。
/**
* @author yuan
* @date 2019/2/19
* @description 最大堆,时间复杂度O(nlgk)
* 用最大堆保存这k个数,每次只和堆顶比,如果比堆顶(堆中最大的元素)小,删除堆顶,新数入堆。
* 否则抛弃
*/
public class 最小的K个数 {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> result = new ArrayList<>(input.length);
int len = input.length;
if (k > len || k < 1) {
return result;
}
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k, Comparator.reverseOrder());
for (int i = 0; i < len; i++) {
if (maxHeap.size() < k) {
maxHeap.offer(input[i]);
} else if (input[i] < maxHeap.peek()) {
// 如果当前整数比堆中最大的元素还小,用这个整数替换已有的最大值
maxHeap.poll();
maxHeap.offer(input[i]);
}
}
result.addAll(maxHeap);
return result;
}
}