注:
题目:
题解:
堆排序:
class Solution {
public:
//调整堆
void maxheaptune(vector<int>& nums,int heapsize,int i){
int left=2*i+1,right=2*i+2,largest=i;
if(left<=heapsize&&nums[left]>nums[largest]){
largest=left;
}
if(right<=heapsize&&nums[right]>nums[largest]){
largest=right;
}
if(largest!=i){
swap(nums[largest],nums[i]);
maxheaptune(nums,heapsize,largest);
}
}
//构建大顶堆
void buildmaxheap(vector<int>& nums,int heapsize){
for(int i=heapsize/2;i>=0;i--){
maxheaptune(nums,heapsize,i);
}
}
int findKthLargest(vector<int>& nums, int k) {
int heapsize=nums.size()-1;
buildmaxheap(nums,heapsize);
//弹出k-1个顶点,则为要找的值
for(int i=1;i<k;i++){
swap(nums[0],nums[heapsize]);
heapsize--;
maxheaptune(nums,heapsize,0);
}
return nums[0];
}
};
分治法快速排序:
分解: 将数组 a[l⋯r] 「划分」成两个子数组a[l⋯q−1]、a[q+1⋯r],使得 ]a[l⋯q−1] 中的每个元素小于等于 a[q],且 a[q] 小于等于a[q+1⋯r] 中的每个元素。其中,计算下标q 也是「划分」过程的一部分。
解决: 通过递归调用快速排序,对子数组 a[l⋯q−1] 和 a[q+1⋯r] 进行排序。
合并: 因为子数组都是原址排序的,所以不需要进行合并操作,a[l⋯r] 已经有序。
上文中提到的 「划分」 过程是:从子数组 a[l⋯r] 中选择任意一个元素 x 作为主元,调整子数组的元素使得左边的元素都小于等于它,右边的元素都大于等于它, x 的最终位置就是 q。
由此可以发现每次经过「划分」操作后,我们一定可以确定一个元素的最终位置,即 x 的最终位置为 q,并且保证a[l⋯q−1] 中的每个元素小于等于 a[q],且 a[q] 小于等于 a[q+1⋯r] 中的每个元素。所以只要某次划分的 q 为倒数第 k 个下标的时候,我们就已经找到了答案。 我们只关心这一点,至于 a[l⋯q−1] 和 a[q+1⋯r] 是否是有序的,我们不关心。
因此我们可以改进快速排序算法来解决这个问题:在分解的过程当中,我们会对子数组进行划分,如果划分得到的 q 正好就是我们需要的下标,就直接返回 a[q];否则,如果q 比目标下标小,就递归右子区间,否则递归左子区间。这样就可以把原来递归两个区间变成只递归一个区间,提高了时间效率。这就是「快速选择」算法。
class Solution {
public:
int result;
void merge(vector<int>& nums,int left,int right,int index){
int q=partition(nums,left,right);
if(q==index){
result=nums[q];
}
else{
//分治(注意两个函数里面的参数)
q<index?merge(nums,q+1,right,index):merge(nums,left,q-1,index);
}
}
int partition(vector<int>& nums,int left,int right){
//令nums[right]为本次求出的分治位置
int x=nums[right];
//i用来记录当前比x小的序列的结尾位置
int i=left-1;
for(int j=left;j<right;j++){
if(nums[j]<=x){
++i;
swap(nums[i],nums[j]);
}
//将a[right]的位置放到i后一位,此时a[right]左边的值都比他小,右边的值都比他大
}
swap(nums[i+1],nums[right]);
return i+1;
}
int findKthLargest(vector<int>& nums, int k) {
merge(nums, 0, nums.size() - 1, nums.size() - k);
return result;
}
};