剑指 Offer 40. 最小的k个数 #
- 标签:数组、分治、快速选择、排序、堆(优先队列)
- 难度:简单
题目大意 #
描述:给定整数数组 arr
,再给定一个整数 k
。
要求:返回数组 arr
中最小的 k
个数。
说明:
- $0 \le k \le arr.length \le 10000$。
- $0 \le arr[i] \le 10000$。
示例:
- 示例 1:
|
|
- 示例 2:
|
|
解题思路 #
直接可以想到的思路是:排序后输出数组上对应的最小的 k 个数。所以问题关键在于排序方法的复杂度。
冒泡排序、选择排序、插入排序时间复杂度 $O(n^2)$ 太高了,解答会超时。
可考虑堆排序、归并排序、快速排序。
思路 1:堆排序(基于大顶堆) #
具体做法如下:
- 使用数组前
k
个元素,维护一个大小为k
的大顶堆。 - 遍历数组
[k, size - 1]
的元素,判断其与堆顶元素关系,如果遇到比堆顶元素小的元素,则将与堆顶元素进行交换。再将这k
个元素调整为大顶堆。 - 最后输出大顶堆的
k
个元素。
思路 1:代码 #
|
|
思路 1:复杂度分析 #
- 时间复杂度:$O(n\log_2k)$。
- 空间复杂度:$O(1)$。
思路 2:快速排序 #
使用快速排序在每次调整时,都会确定一个元素的最终位置,且以该元素为界限,将数组分成了左右两个子数组,左子数组中的元素都比该元素小,右子树组中的元素都比该元素大。
这样,只要某次划分的元素恰好是第 k
个元素下标,就找到了数组中最小的 k
个数所对应的区间,即 [0, k - 1]
。 并且我们只需关注第 k
个最小元素所在区间的排序情况,与第 k
个最小元素无关的区间排序都可以忽略。这样进一步减少了执行步骤。
思路 2:代码 #
|
|
思路 2:复杂度分析 #
- 时间复杂度:$O(n)$。证明过程可参考「算法导论 9.2:期望为线性的选择算法」。
- 空间复杂度:$O(\log_2 n)$。递归使用栈空间的空间代价期望为 $O(\log_2n)$。