【LeetCode-数组】数组中的第K个最大元素
题目描述
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:
- 你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。
题目链接: https://leetcode-cn.com/problems/kth-largest-element-in-an-array/
思路1
升序排序,然后输出第 n-k 个元素(第 k 大)。代码如下:
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
sort(nums.begin(), nums.end());
return nums[nums.size()-k];
}
};
- 时间复杂度:O(nlogn)
快速排序的时间复杂度。 - 空间复杂度:O(1)
思路2
使用小根堆,小根堆的堆顶元素是堆中最小的元素。所以,我们遍历数组,将数组中的元素加入堆中,然后保持堆的大小为 k,这样数组遍历结束后的堆中的元素就是数组中前 k 大的元素,堆顶元素是堆中最小元素,也就是第 k 大的元素。代码如下:
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
priority_queue<int, vector<int>, greater<int>> q;
for(int i=0; i<nums.size(); i++){
q.push(nums[i]);
if(q.size()>k) q.pop();
}
return q.top();
}
};
- 时间复杂度:O(nlogk)
向堆中添加一个元素的时间复杂度为 logk,添加 n 次,所以为 nlogk。 - 空间复杂度:O(k)
思路3
使用快速排序中的 partition 过程来做。partition 过程如下:
- 找一个比较的基准,例如选取数组的第一个元素;
- 设置两个指针 i=0,j=nums.size()-1;
- 如果 i<j,循环:
- 从数组尾从后向前遍历,找到第一个小于基准的元素 nums[i];
- 从数组头从前向后遍历,找到第一个大于基准的元素 nums[j];
- 交换 nums[i] 和 nums[j];
- 交换 nums[i] 和 nums[left]。
一次 partition 之后,nums[i] 之前的数字都小于等于 nums[i],nums[i] 之后的数字都大于等于 nums[i],nums[i] 的位置已经确定,也就是 nums[i] 是数组 nums 中第 i 小的元素,如果 i==nums.size()-k(第 k 大元素),则返回 nums[i],否则根据 i 和 nums.size()-k 的大小关系,使用类似于二分查找的方法缩小 partition 的范围。具体代码如下:
class Solution {
public:
int ans = -1;
int findKthLargest(vector<int>& nums, int k) {
int left = 0;
int right = nums.size()-1;
int target = nums.size()-k;
while(true){ // while(left<=right) 也行
int idx = partition(nums, left, right);
if(idx==target) return nums[idx];
else if(idx<target){
left = idx+1;
}else if(idx>target){
right = idx-1;
}
}
return -1;
}
int partition(vector<int>& nums, int left, int right){
int base = nums[left];
int i = left;
int j = right;
while(i<j){
while(nums[j]>=base && i<j) j--;
while(nums[i]<=base && i<j) i++;
swap(nums[i], nums[j]);
}
swap(nums[i], nums[left]);
return i;
}
};
这种写法参考了这篇题解。
- 时间复杂度:O(n)
- 空间复杂度:O(1)