LeetCode从低效到高效,点击
一、题目描述:
题目要求
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
来源:力扣(LeetCode)链接
示例
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
二、思路分析:
本题是一道很经典的排序相关的题目,后面很多题都是这个思路。 思路
- 排序,访问第k大的数,这种思路没啥好说的 也没啥好写的
- 快速排序的每次Partition都可以确定一个位置的值,那么我们只要找到倒数第k个位置的值就可以了,并且可以修改快速排序,不用排完整个顺序,外面套一个二分查找来取快速定位
- 使用小根堆,先放进k个数,然后每次放进去都去掉一个小的,这样都扔进去之后的堆顶就是第k大的数
三、AC 代码:
先敲一个快排维持手感。。
// 快速排序 左闭右闭
int Partition(vector<int> &arr,int left,int right){
// 这里使用第一个值作为起始flag
int temp = arr[left];
while(left<right){
while(left<right && arr[right]>=temp) right--;
arr[left] = arr[right];
while(left<right && arr[left]<=temp) left++;
arr[right] = arr[left];
}
arr[left] = temp;
return left;
}
void QuickSort(vector<int> &arr,int left,int right){
if(left<right){
int mid = Partition(arr,left,right);
QuickSort(arr,mid+1,right);
QuickSort(arr,left,mid-1);
}
}
快速排序+二分查找:
// 48 ms 9.8 MB
// 快速排序用来确定一个元素位置
int Partition(vector<int>& nums,int left,int right){
// 加上两行随机后性能提升明显,这测试用例坏的很啊 8 ms 9.6 MB
int keyLoc = left + rand() % (right - left);
swap(nums[keyLoc], nums[left]);
int temp = nums[left];
while(left<right){
while(left<right&&nums[right]>=temp) right--;
nums[left] = nums[right];
while(left<right&&nums[left]<=temp) left++;
nums[right] = nums[left];
}
nums[left] = temp;
return left;
}
void QuickSortK(vector<int>& nums,int left,int right,int k){
if(left<right){
// mid是已经位置确定的元素,这里不用快排都进行排序,使用二分的思路,在乱序数组中用Partition定位中间值
int mid = Partition(nums,left,right);
if(mid==k) return;
else if(mid>k){
QuickSortK(nums,left,mid-1,k);
}else{
QuickSortK(nums,mid+1,right,k);
}
}
}
int findKthLargest_v0(vector<int>& nums, int k) {
int l = nums.size()-1;
QuickSortK(nums,0,l,l-k+1);
return nums[nums.size()-k];
}
小根堆:
// 使用小根堆的方式 8 ms 9.9 MB
int findKthLargest(vector<int>& nums, int k) {
int len = nums.size();
// 创建小根堆,小根堆中小的在上面,维护一个k大小的小根堆,扔掉更小的,那么小根堆顶就是保存的第k大的数
priority_queue<int,vector<int>,greater<int>> min_heap;
int i=0;
for(;i<k;i++) min_heap.emplace(nums[i]);
for(;i<len;i++){
if(nums[i]>min_heap.top()){
// 这里能确定nums[i]比堆顶大,所以可以先把对顶丢了,再加入nums[i] 这样快一点
min_heap.pop();
min_heap.emplace(nums[i]);
}
}
return min_heap.top();
}
四、总结:
使用快排+二分的结果与使用小根堆的效果差不多,如果时间紧迫,使用小根堆就可以了。
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情