[LeetCode]剑指 Offer 40. 最小的k个数
Posted Spring-_-Bear
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[LeetCode]剑指 Offer 40. 最小的k个数相关的知识,希望对你有一定的参考价值。
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入 4、5、1、6、2、7、3、8 这 8 个数字,则最小的 4 个数字是 1、2、3、4。
示例 1:
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:
输入:arr = [0,1,2,1], k = 1
输出:[0]
限制:
- 0 <= k <= arr.length <= 10000
- 0 <= arr[i] <= 10000
题解一:
/**
* 剑指 Offer 40. 最小的k个数
*/
public int[] getLeastNumbers(int[] arr, int k)
// 排序后返回数组的前 k 个数即可
Arrays.sort(arr);
return Arrays.copyOf(arr, k);
题解二:
利用 快速排序 算法(升序)基准数左边的数均小于基准数的特点,
对原数组进行快速排序,若在某一趟快排的过程中基准数左边(含基准数)的元素个数等于 k 则直接返回子数组即可(题目未要求返回的元素顺序)。
/**
* 剑指 Offer 40. 最小的k个数
*/
public int[] getLeastNumbers(int[] arr, int k)
/*
* 利用快速排序(升序)基准数左边的数均小于基准数的特点,
* 对原数组进行快速排序,在快排的过程中若某一趟过程中基准数左边(含基准数)的元素个数等于 k 则直接返回子数组即可
*/
return k >= arr.length ? arr : quickSort(arr, 0, arr.length - 1, k);
public int[] quickSort(int[] arr, int start, int end, int k)
int left = start;
int right = end;
// 每次快排选取最左边的数作为基准数
int baseNum = arr[start];
while (left < right)
// 从右往左寻找不大于基准数的数
while (right > left && arr[right] >= baseNum)
right--;
// 从左往右寻找不小于基准数的数
while (left < right && arr[left] <= baseNum)
left++;
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
// 移动基准数
arr[start] = arr[left];
arr[left] = baseNum;
// 左序列比基准数小的元素个数大于 k,左序列继续快排
if (left > k)
return quickSort(arr, start, left - 1, k);
// 左序列比基准数小的元素个数小于 k,右序列快排
if (left < k)
return quickSort(arr, left + 1, end, k);
return Arrays.copyOf(arr, k);
题解三:
-
大顶堆(升序):每个父节点的值都大于或等于其左右孩子节点的值(并未要求左右孩子值的大小关系)
-
小顶堆(降序):每个父节点的值都小于或等于其左右孩子节点的值(并未要求左右孩子值的大小关系)
题目要求找出数组中前 K 个小的数,因此用一个容量为 K 的大根堆,每次 poll 出最大的数,那堆中保留的就是前 K 小啦(注意不是小根堆!小根堆的话需要把全部的元素都入堆,时间复杂度是 O(NlogN)
而不是 O(NlogK)
)
此方法较快排慢,但 Java 中提供了现成的 PriorityQueue(默认小根堆),实现起来最为简单。
/**
* 剑指 Offer 40. 最小的k个数
*/
public int[] getLeastNumbers(int[] arr, int k)
/*
* 保持堆的大小为 K,遍历数组中的元素,遍历的时做如下判断:
* 1. 若堆的大小小于 K,当前数字入堆
* 2. 否则判断当前数字与大根堆堆顶元素的大小关系,如果当前数字比大根堆堆顶还大,这个数就直接跳过
* 3. 反之先 poll 掉堆顶,再将该数字放入堆顶
* 当遍历完数组中的元素时就可以保证留在大根堆中 K 个数是数组中最小的 K 个数
*/
if (k <= 0 || arr.length == 0)
return new int[0];
// 默认是小根堆,实现大根堆需要重写比较器
Queue<Integer> heap = new PriorityQueue<>((v1, v2) -> v2 - v1);
for (int num : arr)
// 若堆的大小小于 K,当前数字入堆
if (heap.size() < k)
heap.offer(num);
else if (num < heap.peek())
// 当前数字比大根堆堆顶小,弹出堆顶元素,当前元素入堆
heap.poll();
heap.offer(num);
// 返回堆中的元素即是数组中最小的 K 个数
int[] res = new int[k];
int idx = 0;
for (int num : heap)
res[idx++] = num;
return res;
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/zui-xiao-de-kge-shu-lcof
以上是关于[LeetCode]剑指 Offer 40. 最小的k个数的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode(剑指 Offer)- 40. 最小的 k 个数