215.数组中的第K个最大值
原题
堆 分治算法
第一反应,先排序,再倒着遍历,暴力直接找到最大值,时间复杂度O(nlogn)。
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
sort(nums.begin(),nums.end());
int index=nums.size()-1;
while(k>1) {
index--;
k--;
}
return nums[index];
}
};
咦????竟然没有超时……
官答有两种解答方法:
方法一:快速选择算法
根据快速排序算法加以改动:已知快排每次划分后确定一个元素的正确位置,那么根据题目来说,我们需要确定的是第nums.size()-k个位置的正确元素。使用递归的算法,每次确定区间(l, r)中q位置的元素,如果q==k,就返回该元素,如果q<k,就在区间(q+1, r)进行迭代,否则就在区间(l, q-1)进行迭代。
如果每次划分都分成区间长度为1和n-1的两个区间,那么时间复杂度会是O(n^2),为了避免这种情况,这里使用随机划分区间。
class Solution {
public:
int selectQuick(vector<int>& nums, int l, int r, int k) {
int q=randomPartition(nums, l, r);
if(q==k) return nums[q];
return q<k?selectQuick(nums, q+1, r, k):selectQuick(nums, l, q-1, k);
}
int randomPartition(vector<int>& nums, int l,int r) {
int i=rand()%(r-l+1)+l;
swap(nums[i], nums[r]);//将待定位元素放到最后
return Partiton(nums, l, r);
}
int Partiton(vector<int>& nums, int l, int r) {
int m=nums[r], index=l-1;
for(int i=l;i<r;i++){
if(nums[i]<=m){
//将该元素的值放到左边
swap(nums[++index],nums[i]);
}
}
swap(nums[++index],nums[r]);//将数值m放在正确的位置:index上面
return index;
}
int findKthLargest(vector<int>& nums, int k) {
return selectQuick(nums, 0, nums.size()-1, nums.size()-k);
}
};
这样时间复杂度是O(n),空间复杂度是O(logn)【递归使用栈的空间代价为O(logn)】
方法二:基于堆排序的选择方法
建立一个最大堆,后删除k-1次,就得到了目标值。
class Solution {
public:
//利用数组构造堆
void maxHeapify(vector<int>& nums, int i, int heapSize) {
int l=2*i+1, r=2*i+2, largeset=i;
if(l<heapSize&&nums[l]>nums[largeset]) largeset=l;
if(r<heapSize&&nums[r]>nums[largeset]) largeset=r;
if(largeset!=i) {
swap(nums[largeset],nums[i]);
maxHeapify(nums, largeset, heapSize);
}
}
void buildHeap(vector<int>& nums, int heapSize) {
for(int i=heapSize/2; i>=0;i--)
maxHeapify(nums, i, heapSize);
}
int findKthLargest(vector<int>& nums, int k) {
int heapSize=nums.size();
buildHeap(nums, heapSize);
for(int i=nums.size()-1;i>=nums.size()-k+1;i--){
swap(nums[0],nums[i]);
--heapSize;
maxHeapify(nums, 0, heapSize);
}
return nums[0];
}
};
时间复杂度是O(n+klogn)渐进于O(nlogn),空间复杂度:使用递归——O(logn)。