题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
解题思路分析
- 这道题有个很明显的方法,先排序然后直接取k个数,但是这肯定不是人家想要的答案
- 这道题可以使用快排的思想来解答,同时也可使用堆的思想来解答
- 使用快排的思想每次选择一个base,使得比base大的数放到base的左边,比base小的数放到base的右边,我们只要找到base的下标为k时,那么该base的左边k个数就是最小的
- 通过堆的方法就是先使用k个数建立一个大顶堆,然后从k+1个数开始,分别判断k+1个数是否小于当前堆顶元素,小于则入堆,并重新调整堆,然后就可以很轻松的获得最小的k个数
代码实现
- 快排思想
public ArrayList<Integer> getLeastKNumbers(int[] input, int k) {
if (input == null || input.length <= 0 || k <= 0) {
return null;
}
ArrayList<Integer> list = new ArrayList<>();
int start = 0, end = input.length - 1;
int i = partition(input, start, end);
//循环找到第k + 1大的数
while (i != k) {
if (i > k) {
end = i - 1;
i = partition(input, start, end);
} else {
start = i + 1;
i = partition(input, start, end);
}
}
for (int j = 0; j < k; j++) {
list.add(input[j]);
}
return list;
}
private int partition(int[] nums, int start, int end) {
int base = nums[start], i = start, j = end;
while (i < j) {
//从后往前遍历,直到找到比base小的值
while (i < j && nums[j] > base) {
j--;
}
if (i < j) {
nums[i] = nums[j];
i++;
//从前往后遍历,直到找到比base大的值
while (i < j && nums[i] < base) {
i++;
}
if (i < j) {
nums[j] = nums[i];
j--;
}
}
}
nums[i] = base;
//返回base的下标
return i;
}
- 堆思想
public ArrayList<integer> getLeastKNumbers(int[] input, int k) {
if (input == null || input.length <= 0 || k <= 0) {
return null;
}
ArrayList<Integer> list = new ArrayList<>();
//使用优先级队列来实现一个大顶堆,默认时是小顶堆
PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k, (o1, o2) -> o2 - o1);
for (int i = 0, i < k; i++) {
//先将前k个数入堆
maxHeap.offer(input[i]);
}
//然后向后遍历,碰到比堆顶小的元素就入堆
for (int i = k; i < input.length; i++) {
if (input[i] < maxHeap.peek()) {
maxHeap.poll();
maxHeap.offer(input[i]);
}
}
//放入list中返回
for (int i = 0; i < k; i++) {
list.add(maxHeap.poll());
}
return list;
}