一、题目描述
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入: [3,2,1,5,6,4], k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4
提示:
1 <= k <= nums.length <= 105
-104 <= nums[i] <= 104
分析
这道题可以使用 快速排序的思想 来做。
如果我们直接把整个数组排成降序,然后返回 nums[k - 1],时间复杂度是:O(n log n)
因此可以使用 快速选择 Quick Select。
快速选择和快速排序非常像:
快速排序:每次 partition 后,左右两边都继续排序
快速选择:每次 partition 后,只处理目标下标所在的一边
如果数组按降序排列,那么第 k 大元素的位置就是:int target = k - 1;
每次随机选择一个基准值 p,然后把数组划分成三部分(三路划分 ):
//大于 p 的区域 | 等于 p 的区域 | 小于 p 的区域
[left, l] > p //如果 target <= l,说明答案在左边大于 p 的区域
[l + 1, r - 1] == p //否则,说明 target 落在等于 p 的区域,直接返回 p
[r, right] < p //如果 target >= r,说明答案在右边小于 p 的区域
具体代码相交于普通的快排有两个优化:
- 使用随机数来获得基准元素
- 三路划分优化:按照基准元素,将所有元素分成三个区间。左部分全部⼩于 pivot,中间部分全部等于 pivot,右部分全部⼤于 pivot。然后中间部分就不⽤管了,直接递归处理左右部分。
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>
using namespace std;
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
srand(time(0));
int target = k - 1; // 第 k 大元素在降序数组中的位置
int left = 0;
int right = nums.size() - 1;
while (left <= right) {
int l = left - 1;
int i = left;
int r = right + 1;
// 随机选择一个基准值
int p = get_random(nums, left, right);
// 三路划分:
// [left, l] > p
// [l + 1, r - 1] == p
// [r, right] < p
while (i < r) {
if (nums[i] > p) {
swap(nums[i++], nums[++l]);
} else if (nums[i] == p) {
i++;
} else {
swap(nums[--r], nums[i]);
}
}
if (target <= l) {
right = l;
} else if (target >= r) {
left = r;
} else {
return p;
}
}
return -1;
}
int get_random(vector<int>& nums, int left, int right) {
return nums[rand() % (right - left + 1) + left];
}
};

1453

被折叠的 条评论
为什么被折叠?



