215. 数组中的第K个最大元素/C++

本文介绍了一种使用快速排序的二分技术及小顶堆数据结构来查找数组中第K大的元素的方法。通过partition函数进行数组的划分,使元素能够按大小分为左右两部分,从而快速定位到目标元素。同时,利用小顶堆保持堆中元素的数量为K,以高效找到第K大的元素。

在这里插入图片描述

利用快速排序的二分技术

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        int low=0,mid=0,high=nums.size()-1;
        while(low<=high){
            mid=partation(nums,low,high);
            if(mid==k-1) return nums[mid];
            else if(mid>k-1) high=mid-1;
            else low=mid+1;
        }
        return -1;
    }
    //返回[++++====----]
    //返回        ^返回与枢轴相等的最后一个
    int partation(vector<int>& nums,int start,int end){
        int left=start,right=end;
        swap(nums[left],nums[(left+right)/2]);//以中间的值为枢轴,可以加快效率
        int pivot=nums[left];
        while(left<right){
            while(left<right && nums[right]<pivot) --right;
            if(left<right) nums[left++]=nums[right];
            while(left<right && nums[left]>=pivot) ++left;
            if(left<right) nums[right--]=nums[left];
        }
        nums[left]=pivot;
        return left;
    }
};

小顶堆

int findKthLargest(vector<int>& nums, int k) {
    priority_queue<int, vector<int>, greater<int>> minHeap;
    for (auto e : nums) {
        if (minHeap.size() == k) {
            if (e < minHeap.top()) continue;
            minHeap.pop();
        }
        minHeap.push(e);
    }
    return minHeap.top();
}
<think>好的,我现在需要帮助用户找到C++实现数组中第K个最大元素的方法。用户之前的问题是关于负数标记法的原理,但现在转向了另一个算法问题,即TOP-K问题。我需要结合用户提供的引用内容来给出合适的回答。 首先,用户提供的引用1到5都提到了使用小顶堆(优先队列)的方法,以及可能的快排思想。用户明确要求C++实现,所以我需要重点讲解这两种方法,并确保代码示例正确。 接下来,我需要确定用户的需求是否包括时间复杂度为O(n)的算法,因为引用1中提到必须设计时间复杂度为O(n)的算法。但根据常见的算法,快速选择算法平均是O(n),而堆方法是O(n log k)。需要区分这两种方法,并解释各自的优缺点。 用户可能对这两种方法的实现细节和复杂度比较感兴趣。我需要检查引用中的代码示例,特别是引用2、3、4中的优先队列实现,确保代码的正确性,并可能进行优化或解释。 另外,用户之前的回答中提到了负数标记法,这可能与当前问题无关,但需要保持回答的结构清晰,符合用户之前设定的格式,比如分步骤解析、代码示例、复杂度分析等。 可能的疑问点:用户是否了解优先队列的底层实现?是否需要解释小顶堆为什么能解决这个问题?或者是否需要比较不同方法的适用场景? 需要确保引用的正确使用,比如引用1提到必须O(n)时间,但堆方法并不满足,可能需要说明为什么引用中存在这样的矛盾,或者用户提供的引用可能有误。但根据问题描述,用户可能希望得到实际可行的C++实现,而快速选择虽然平均O(n),但最坏情况是O(n²),所以需要权衡。 总结,回答应包括两种主要方法:基于优先队列的小顶堆和快速选择算法,分别说明它们的实现步骤、代码示例、复杂度分析,以及优缺点比较。同时,需要参考用户提供的引用内容,尤其是引用2、3、4中的代码,确保示例正确,并可能调整格式或解释以更清晰。</think>#### 一、问题定义与算法选择 **问题描述**:给定数组 $nums$ 和整数 $k$,要求找到数组中第 $k$ 个最大元素,且需满足时间复杂度 $O(n)$ 或接近线性复杂度[^1]。 **核心方法对比**: | 方法 | 时间复杂度 | 空间复杂度 | 适用场景 | |----------------|------------------|------------|------------------------| | 优先队列(堆) | $O(n \log k)$ | $O(k)$ | 动态数据流或内存受限 | | 快速选择 | 平均 $O(n)$ | $O(1)$ | 静态数组且需原地操作 | --- #### 二、优先队列法(小顶堆) ##### 1. 算法原理 - **小顶堆特性**:始终维护容量为 $k$ 的最小堆,堆顶为当前堆中最小元素(即第 $k$ 大的候选值)[^2][^3] - **筛选机制**:遍历数组时,若新元素大于堆顶,则替换堆顶并调整堆结构,确保堆中始终保留最大的 $k$ 个元素[^4] ##### 2. C++ 实现代码 ```cpp #include <vector> #include <queue> using namespace std; int findKthLargest(vector<int>& nums, int k) { priority_queue<int, vector<int>, greater<int>> minHeap; // 小顶堆 for (int num : nums) { if (minHeap.size() < k) { minHeap.push(num); } else if (num > minHeap.top()) { minHeap.pop(); minHeap.push(num); } } return minHeap.top(); } ``` ##### 3. 复杂度分析 - **时间复杂度**:$O(n \log k)$,每个元素最多触发一次堆调整(调整代价 $O(\log k)$)[^3] - **空间复杂度**:$O(k)$,仅需存储 $k$ 个元素的堆 --- #### 三、快速选择法(基于快速排序思想) ##### 1. 算法原理 - **分治策略**:通过基准值(pivot)将数组分为左右两部分,根据左右区间长度确定目标元素所在区间[^5] - **数学表达**: $$ \text{若基准值索引 } p \text{ 满足 } p = n - k \text{,则 } nums[p] \text{ 即为所求} $$ ##### 2. C++ 实现代码 ```cpp #include <vector> #include <algorithm> using namespace std; int partition(vector<int>& nums, int left, int right) { int pivot = nums[right]; int i = left; for (int j = left; j < right; j++) { if (nums[j] <= pivot) { swap(nums[i], nums[j]); i++; } } swap(nums[i], nums[right]); return i; } int findKthLargest(vector<int>& nums, int k) { int n = nums.size(); int target = n - k; // 第k大元素在排序后的索引 int left = 0, right = n - 1; while (left <= right) { int p = partition(nums, left, right); if (p == target) { return nums[p]; } else if (p < target) { left = p + 1; } else { right = p - 1; } } return -1; } ``` ##### 3. 复杂度分析 - **时间复杂度**:平均 $O(n)$,最坏 $O(n^2)$(可通过随机化基准值优化) - **空间复杂度**:$O(1)$(原地操作) --- #### 四、方法对比与选型建议 | 对比维度 | 优先队列法 | 快速选择法 | |----------------|--------------------------|--------------------------| | **时间复杂度** | 稳定 $O(n \log k)$ | 平均 $O(n)$ | | **空间占用** | 需额外存储 $k$ 个元素 | 原地操作,无额外空间 | | **适用性** | 动态数据流处理 | 静态数组快速查询 | | **代码复杂度** | 简单,直接调用STL | 需手动实现分区逻辑 | --- #### 五、扩展优化与常见问题 ##### 1. 优先队列法的改进 - **预筛选优化**:当 $k > n/2$ 时,改用大顶堆寻找第 $(n - k + 1)$ 小元素,减少堆操作次数[^4] - **批量插入**:若数组分段有序,可批量插入元素减少调整次数 ##### 2. 快速选择的随机化优化 ```cpp int partition_random(vector<int>& nums, int left, int right) { int random_pivot = left + rand() % (right - left + 1); swap(nums[random_pivot], nums[right]); return partition(nums, left, right); } ``` ##### 3. 常见问题解答 **Q1:如何处理重复元素?** - 两种方法均自动处理重复值,无需特殊逻辑[^5] **Q2:当 $k=1$ 或 $k=n$ 时性能如何?** - $k=1$ 时,优先队列法退化为 $O(n)$(仅需一次遍历找最大值) - $k=n$ 时,优先队列法退化为堆排序,时间复杂度 $O(n \log n)$ **Q3:为何快速选择法最坏情况是 $O(n^2)$?** - 当每次分区极度不平衡时(如数组已有序且基准值总选端点),需递归 $n$ 层,每层操作 $O(n)$[^5] ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值