2021-3-7 215. 数组中的第K个最大元素(堆排序,分治法快速排序)

本文介绍了两种高效算法——堆排序和快速选择算法,用于找到未排序数组中的第K大元素。通过构建最大堆和改进的快速排序实现,提供详细的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注:

题目:

题解:

堆排序:

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;
    }
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值