算法实战:快速选择法解决第 K 大元素与最小 K 个数问题
在算法面试中,寻找第 K 大元素或最小 K 个数是高频考点。如果直接排序,时间复杂度为 O(NlogN),对于海量数据来说开销较大。这里介绍一种更高效的解法——快速选择算法(Quick Select)。它借鉴了快速排序的分区思想,但只递归处理包含目标的那一侧,平均时间复杂度可降至 O(N)。下面结合两道经典题目具体说明。
45. 数组中的第 k 个最大元素
题目描述
给定整数数组 nums 和整数 k,请返回该数组中第 k 个最大的元素。
注意需要的是有序序列中的第 k 个位置,而不是第 k 个不同的元素。
算法思路
传统的快排是将数组分为小于基准、等于基准、大于基准三部分。利用这个特性,我们可以计算每个区间内元素的个数,从而判断目标值落在哪个区间。
- 三路划分:将数组分为
[left, l](大于 key)、[l+1, r-1](等于 key)、[r, right](小于 key)。 - 定位区间:
- 若右边区域(小于 key)元素个数 >= k,说明第 k 大的数在右边,继续递归。
- 若中间区域(等于 key)加右边区域个数 >= k,说明第 k 大的数就是 key。
- 否则,目标在左边区域,调整 k 的值继续递归。
这种策略避免了完全排序,只在必要的一侧进行递归。
C++ 实现
class Solution {
public:
int Top_k(vector<int>& nums, int left, int right, int k) {
if(left == right) {
return nums[left];
}
// 随机选择基准元素,避免最坏情况
int key = nums[rand() % (right - left + 1) + left];
int l = left - 1, r = right + 1, i = left;
// 三路划分逻辑
while(i < r) {
if(nums[i] > key) {
swap(nums[i], nums[--r]);
} else if(nums[i] < key) {
swap(nums[i++], nums[++l]);
} else {
i++;
}
}
rightCount = right - r + ;
midCount = r - l - ;
(rightCount >= k) {
(nums, r, right, k);
} (rightCount + midCount >= k) {
key;
} {
(nums, left, l, k - (rightCount + midCount));
}
}
{
(());
(nums, , nums.() - , k);
}
};




