寻找第k大

本文探讨了如何利用快速排序和堆排序的思想解决查找数组中第k大数的问题。首先介绍了快速排序的优化策略,然后通过堆排序的方法实现,通过交换操作找到第k个最大元素。两种方法对比,揭示了堆排序在效率上的优势。

寻找第k大-笔记

  • 题目提示使用快速排序的思想找到一个数组中第k大的数。
  • 思路:
    • 快排思想:使用快排的思想,就是将数组按照基准值划分为左右两边,左边大于基准,先进行一次快排,基准防止的位置为p,将大于基准的值放在前半部分,小于基准的值放在后半部分。大于基准的值有p-left个。所以此时如果p-left+1==k表示此时基准就是第k大的数。如果p-left+1 < k说明第k大在后半段(此时要找后半段中第k-(p-left+1))大的值,如果p-left+1 > k则说明第k大在前半段。但是这代码在牛客上超时了。
    • 使用堆排序,只交换k次堆顶和末尾元素,然后返回第k大的元素。

代码

  • 快排思想
class Solution {
public:
  
    int findpivot(vector<int> &nums,int left,int right){
        int i=left,j=right;
        int pivot=nums[left];
        while(left<right){
            while(left<right && nums[right]<pivot){
                right--;
            }
            nums[left]=nums[right];
            
            while(left<right && nums[left]>=pivot){
                left++;
            }
            nums[right]=nums[left];
        }
        int res=0;
        nums[left]=pivot;
        return left;
    }
    
    int dofind(vector<int>& nums,int left,int right,int k){
        
        int p=findpivot(nums,left,right);
        if(p-left+1==k){
            return nums[p];
        }
        else if(p-left+1>k){
            //说明在左半段
            return dofind(nums,left,p-1,k);
        }
        
        //说明在右半段
        return dofind(nums,p+1,right,k-(p-left+1));
    }
    int findKth(vector<int> a, int n, int K) {
        // write code here
        //使用快速排序的思想,先进行一次快排,基准防止的位置为p,将大于基准的值放在前半部分,小于基准的值放在后半部分。大于基准的值有p-left个。
        //所以此时如果p-left+1==k表示此时基准就是第k大的数。如果p-left+1<k说明第k大在后半段(此时要找后半段中第k-(p-left+1))大的值,如果p-left+1>k则说明第k大在前半段。
        
        int left=0,right=n-1;
        return dofind(a,left,right,K);
    
    }
};
  • 堆排序,使用大根堆。
class Solution {
public:
    
    void adjust(vector<int>& nums,int start,int last){
        //从第一个非叶子节点开始,从右至左,从上至下开始调整堆,直到父节点大于两个孩子节点。
        int maxchild=2*start+1;
        while(maxchild<=last){
            if(maxchild+1<=last && nums[maxchild]<=nums[maxchild+1]){
                maxchild+=1;
            }
            
            if(nums[start]<nums[maxchild]){
                swap(nums[start],nums[maxchild]);
                start=maxchild;
                maxchild=2*start+1;
            }
            else{
                return;
            }
        }
    }
    void heapfy(vector<int>& nums){
        int n=nums.size()-1;
        for(int i=(n-1)/2;i>=0;i--){
            adjust(nums,i,n);
        }
    }
    int findKth(vector<int> a, int n, int K) {
        // write code here
        //建堆
        n=n-1;
        heapfy(a);
        for(int i=n;i>=n-K;i--){//交换k次
            swap(a[0],a[i]);
            adjust(a,0,i-1);
        }
        return a[n-K+1];//得到第k大的数
    }
};
在 C++ 中查找数组或容器中第 K 的元素,可以通过多种方法实现,其中一种常见且高效的方式是使用优先队列(堆)[^1]。以下是实现这一功能的详细说明和代码示例。 ### 使用小顶堆查找第 K 元素 可以利用一个小顶堆来维护数组中最的 k 个元素,堆顶元素即为第 k 的元素。这种方法的时间复杂度为 $ O(n \log k) $,空间复杂度为 $ O(k) $。 ```cpp #include <vector> #include <queue> int findKthLargest(std::vector<int>& nums, int k) { // 使用小顶堆 std::priority_queue<int, std::vector<int>, std::greater<int>> tmp; // 将前 k 个元素入堆 for (int i = 0; i < k; ++i) { tmp.push(nums[i]); } // 遍历剩余元素,保持堆中为最的 k 个元素 for (int i = k; i < nums.size(); ++i) { if (nums[i] > tmp.top()) { tmp.pop(); tmp.push(nums[i]); } } return tmp.top(); } ``` ### 使用根堆并执行删除操作 另一种方法是建立一个根堆,然后进行 $ k-1 $ 次删除操作,堆顶元素即为第 k 的元素。此方法的时间复杂度为 $ O(n + k \log n) $,适用于 k 较小的情况。 ```cpp #include <vector> #include <algorithm> int findKthLargestWithMaxHeap(std::vector<int>& nums, int k) { // 构建根堆 std::make_heap(nums.begin(), nums.end()); // 执行 k-1 次删除操作 for (int i = 0; i < k - 1; ++i) { std::pop_heap(nums.begin(), nums.end()); nums.pop_back(); } return nums.front(); } ``` ### 复杂度分析 - **时间复杂度**: - 小顶堆方法:$ O(n \log k) $,其中 $ n $ 是数组长度,$ k $ 是目标位置。 - 根堆方法:构建堆的时间为 $ O(n) $,每次删除操作的时间为 $ O(\log n) $,总时间复杂度为 $ O(n + k \log n) $。 - **空间复杂度**: - 小顶堆方法为 $ O(k) $,根堆方法为 $ O(1) $(原地修改)。 ### 总结 - 如果 k 较小且数组较,建议使用根堆方法。 - 如果 k 接近数组长度或需要频繁查询,建议使用小顶堆方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值