给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题
快速排序的核心包括“哨兵划分” 和 “递归” 。
哨兵划分: 以数组某个元素(一般选取首元素)为基准数,将所有小于基准数的元素移动至其左边,大于基准数的元素移动至其右边。
递归: 对 左子数组 和 右子数组 递归执行 哨兵划分,直至子数组长度为 1 时终止递归,即可完成对整个数组的排序。
下图展示了数组 [2,4,1,0,3,5] 的快速排序流程。
「快速选择」:设 N 为数组长度。根据快速排序原理,如果某次哨兵划分后,基准数的索引正好是 N−k ,则意味着它就是第 k 大的数字 。此时就可以直接返回它,无需继续递归下去了。
一种解决方案是使用「三路划分」,即每轮将数组划分为三个部分:小于、等于和大于基准数的所有元素。这样当发现第 k 大数字处在“等于基准数”的子数组中时,便可以直接返回该元素。
为了进一步提升算法的稳健性,我们采用随机选择的方式来选定基准数。
代码如下:
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
return quickSelect(nums, k);
}
private:
int quickSelect(vector<int>& nums, int k) {
// 随机选择基准数
int pivot = nums[rand() % nums.size()];
// 将大于、小于、等于 pivot 的元素划分至 big, small, equal 中
vector<int> big, equal, small;
for (int num : nums) {
if (num > pivot)
big.push_back(num);
else if (num < pivot)
small.push_back(num);
else
equal.push_back(num);
}
// 第 k 大元素在 big 中,递归划分
if (k <= big.size())
return quickSelect(big, k);
// 第 k 大元素在 small 中,递归划分
if (nums.size() - small.size() < k)
return quickSelect(small, k - nums.size() + small.size());
// 第 k 大元素在 equal 中,直接返回 pivot
return pivot;
}
};