题目描述:
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 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 <= 104
-104 <= nums[i] <= 104来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/kth-largest-element-in-an-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
分析:
快排数组后返回对应元素,关键是手写快排,我已经快疯了。。。。之前每次写快排,都不怎么能写对,尤其是有没有等号的问题,以及是否交换的问题,以及选取基准的问题,以及数组中有没有重复元素的问题。今天折腾了一上午,算是搞明白一点了,但还是记住这个模版吧:
1.基准选数组第一个元素。如果使用随机数进行选择,也请把随机数选择的元素交换到数组头部。
2.
while(left<right&&nums[right]>=target) right--; while(left<right&&nums[left]<=target) left++;
这两个循环语句中,必须带等号!!!
3.每一轮快排结束后,把基准值nums[key]和快排结束位置的值nums[left]进行交换,即:swap(nums[key],nums[left]);
4.数组中无论是否有重复元素,这一套模版都能使用
代码如下:
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
// 快排数组,并取值
quicksort(nums,0,nums.size()-1);
return nums[nums.size()-k];
}
void quicksort(vector<int>& nums,int begin,int end)
{
if(begin>=end) return;
int left=begin;
int right=end;
// 获取随机数,来选取随机基准
// int x=begin+rand()%(end-begin+1);
// swap(nums[left],nums[x]);
// 选取数组第一个元素为基准
int key=begin;
int target=nums[key];
while(left<right)
{
// 必须加等号,否则数组左右两端的数相等时会死循环
while(left<right&&nums[right]>=target) right--;
// 必须加等号,否则数组左右两端的数相等时会死循环
while(left<right&&nums[left]<=target) left++;
// 满足条件时,交换nums[left]和nums[right]
if(left<right)
swap(nums[left],nums[right]);
}
// 将基准交换到应该到达的位置
swap(nums[key],nums[left]);
// 快排左数组
quicksort(nums,begin,left-1);
// 快排右数组
quicksort(nums,left+1,end);
}
};
还有一种做法是使用堆。可以使用c++自带的堆或者手写一个堆。
c++自带的堆解决这道题比较简单,就不多说了,附上代码就行:
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
priority_queue<int,vector<int>,less<int>> q;
for(int num:nums)
{
q.push(num);
}
while(--k)
q.pop();
return q.top();
}
};
手写堆对我来说是第一次,因此有些许困难,看了看题解,总结了几点重要的(针对于使用数组来构建的堆):
1.堆中节点的索引为n,则其父节点的索引为(n-1)/2
2.堆中节点的索引为n,则其左子节点索引为n*2+1,右子节点索引为n*2+2
3.堆中最后一个非叶子节点的索引为(nums.size()-2)/2
4.堆中最重要的函数是adjust_heap(int index),其主要功能是建立以index为根的大顶堆。比较index和其左右子节点的大小,来选择大顶堆的根,然后递归调用adjust_heap,重新维护以其子节点为根的大顶堆。
相关的链接和参考:
https://leetcode-cn.com/problems/kth-largest-element-in-an-array/comments/
代码如下:
class Solution {
public:
// 用数组heap模拟大顶堆
vector<int> heap;
int findKthLargest(vector<int>& nums, int k) {
// 根据nums数组创建堆
createheap(nums);
// 弹出前k-1个头节点
while(--k)
pop();
return heap[0];
}
// 弹出大顶堆头部
void pop()
{
// 交换头节点和尾节点
swap(heap[0],heap[heap.size()-1]);
// 删除尾节点
heap.erase(heap.end()-1);
// 调整因为头节点交换而带来的影响
// 即把头节点交换下去,重新构建大顶堆
adjust_heap(0);
return;
}
// 根据nums创建大顶堆
void createheap(vector<int> &nums)
{
// 复制nums到heap中
for(int num:nums)
heap.push_back(num);
// 从heap中的最后一个非叶子节点开始,调整heap为大顶堆
for(int i=nums.size()-2;i>=0;i--)
{
adjust_heap(i);
}
}
// 该函数的作用是,以index为根,使得该树为一个大顶堆
void adjust_heap(int index)
{
// 得到index节点的左右节点
int left=index*2+1;
int right=index*2+2;
int max=index;
int len=heap.size();
// 比较index和左右节点,得到最大的那个节点
if(left<len&&heap[left]>heap[max])
max=left;
if(right<len&&heap[right]>heap[max])
max=right;
if(index!=max)
{
// index和最大的节点互换,使得最大的节点来当根节点,
swap(heap[max],heap[index]);
// 由于互换节点,以max节点为根的大顶堆被破坏,需要重新维护该大顶堆
adjust_heap(max);
}
return;
}
};