【Hot 100 刷题计划】 LeetCode 215. 数组中的第K个最大元素 | C++ 快速选择与堆排序题解

LeetCode 215. 数组中的第K个最大元素 | C++ 快速选择与小顶堆双解法

📌 题目描述

题目级别:中等

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(N)O(N)O(N) 的算法解决此问题。

  • 示例 1:
    输入: [3,2,1,5,6,4], k = 2
    输出: 5

💡 破题思路:Top K 问题的绝代双骄

面对 Top K 问题,直接调用 sort() 排序(O(Nlog⁡N)O(N \log N)O(NlogN))显然不是面试官想要的。在大厂面试中,这道题的标准答案分为两个方向:追求数学极限的 O(N)O(N)O(N) 解法 ,以及应对海量工程数据的解法

🚀 解法一:快速选择算法 QuickSelect (严格 O(N) 面试满分解)

题目加粗强调了时间复杂度必须是 O(N)O(N)O(N),破局的唯一解法就是快速选择算法(QuickSelect)

它的核心思想脱胎于经典的"快速排序(Quick Sort)":

  1. 区域划分(Partition) :随便挑一个基准值(Pivot),把比它 的数扔到左边,比它的数扔到右边(降序划分)。
  2. 精准剪枝(灵魂所在) :一次划分后,基准值就落在了最终正确的排名位置。如果我们要找的第 K 大元素的索引刚好在左半区,那我们完全不需要去管右半区,直接递归左半区即可!

因为每次都能砍掉大约一半的数据量(N+N/2+N/4+...N + N/2 + N/4 + ...N+N/2+N/4+...),它的时间复杂度收敛成了极其优雅的 O(N)O(N)O(N)!

💻 C++ 代码实现 (Hoare 划分模板)
cpp 复制代码
class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        // 调用 quickSelect,注意题目要求的 k 是第 k 大,对应降序数组的索引是 k - 1
        return quickSelect(nums, 0, nums.size() - 1, k);
    }

private:
    int quickSelect(vector<int>& nums, int l, int r, int k) {
        if (l == r) return nums[l]; // 递归终止条件

        // 1. 选取基准值 pivot,取中间元素可避免在有序数组下退化
        int pivot = nums[l + (r - l) / 2];
        int i = l - 1, j = r + 1;

        // 2. 降序划分过程 (Hoare Partition)
        while (i < j) {
            do i++; while (nums[i] > pivot);
            do j--; while (nums[j] < pivot);
            if (i < j) swap(nums[i], nums[j]);
        }

        // 3. 核心剪枝:判断第 k 大的元素落在哪个区间,只去那一个区间寻找
        if (k - 1 <= j) return quickSelect(nums, l, j, k);
        return quickSelect(nums, j + 1, r, k);
    }
};

🏆 解法二:优先队列 / 小顶堆 (海量数据流最优解)

如果在面试中,面试官加上一个条件:"如果是海量数据(如 100 亿条日志),内存放不下整个数组怎么办?" 这时候 QuickSelect 就失效了,必须请出优先队列(小顶堆)。

核心思想:

我们要维护一个大小始终为 KKK 的小顶堆。

遍历数据流,遇到新元素直接入堆。如果堆的容量超过了 KKK,就把堆里最小的元素(堆顶)踢出去。

大浪淘沙之后,等所有数据遍历完,这个大小为 KKK 的堆里留下的就是全场最大的 KKK 个数,而此时的堆顶,刚好就是这 K 个数里最小的,也就是我们要找的第 K 大的数!

💻 C++ 代码实现 (大小为 K 的小顶堆)
cpp 复制代码
class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        // C++ 默认是大顶堆,通过 greater<int> 定义为小顶堆
        priority_queue<int, vector<int>, greater<int>> minHeap;

        for (int i = 0; i < nums.size(); i++) {
            minHeap.push(nums[i]); // 无脑入堆
            
            // 只要堆的大小超过了 K,就把最小的那个(堆顶)踢出去
            if (minHeap.size() > k) {
                minHeap.pop();
            }
        }

        // 最终堆顶留下的,就是第 K 大的元素
        return minHeap.top();
    }
};
相关推荐
fqbqrr3 小时前
2607C++,使用微软detours勾挂工具
c++
爱刷碗的苏泓舒4 小时前
PPP-AR 中的参考星选取:数学原理、评价指标与切换处理
算法·gnss·模糊度固定·ppp-ar·星间单差·参考星·卫星端偏差
蓝悦无人机6 小时前
C++基础 — 函数总结
开发语言·c++
烬羽6 小时前
递归老写崩?一个"退回"公式,把回溯题变成填空题
javascript·深度学习·算法
闪电悠米7 小时前
力扣hot100-41.缺失的第一个正数-原地哈希详解
数据结构·算法·哈希算法
星空露珠7 小时前
28种颜色对应名称,
开发语言·数据库·算法·游戏·lua
我不是懒洋洋8 小时前
从零实现一个分布式监控:Prometheus的核心设计
c++
An_s8 小时前
c++对接pdfium(一)win系统篇
开发语言·c++
众少成多积小致巨8 小时前
C++ 规范参考(上)
c++
QXWZ_IA9 小时前
桥梁数字孪生怎么落地?
人工智能·科技·算法·智能硬件·政务