数组中的第K个最大元素

cpp 复制代码
class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        // 入口函数,直接调用递归辅助函数
        return quickSelect(nums, k);
    }
    
private:
    int quickSelect(vector<int>& nums, int k) {
        // 1. 随机化选择基准数 (Pivot),防止在面对有序数组时退化到 O(N^2)
        int pivot = nums[rand() % nums.size()];

        // 2. 三路划分:准备三个桶,分别存放大于、等于、小于基准数的元素
        vector<int> big, equal, small;
        for (int num : nums) {
            if (num > pivot)
                big.push_back(num);    // 比基准数大的放这里
            else if (num < pivot)
                small.push_back(num);  // 比基准数小的放这里
            else
                equal.push_back(num);  // 和基准数一样的放这里
        }

        // 3. 核心逻辑:判断第 k 大的元素落在哪个桶里

        // 情况 A:如果 big 桶的大小大于等于 k,说明第 k 大一定在 big 桶里
        if (k <= big.size())
            return quickSelect(big, k);

        // 情况 B:如果 nums 总量减去 small 桶的大小小于 k
        // 这意味着:k 落在 big 桶和 equal 桶之外,即在 small 桶里
        // (等价于 k > big.size() + equal.size())
        if (nums.size() - small.size() < k)
            // 递归 small 桶时,k 需要减去已经排除掉的较大元素的个数
            return quickSelect(small, k - (nums.size() - small.size()));

        // 情况 C:k 既不在 big 也不在 small,那它一定就在 equal 桶里
        // 既然 equal 桶里的数都等于 pivot,直接返回 pivot 即可
        return pivot;
    }
};

没关系,这个算法逻辑确实绕。我们丢掉复杂的代码,用打牌 或者分球的例子来直观理解。

快速选择的核心目标是:不排好序,也能定位。

1. 直观比喻:分大小堆

假设有一堆乱七八糟的数字球:[3, 2, 1, 5, 6, 4],我们要找第 2 大的球。

  1. 随便抓一个球作为"基准" :比如抓到了 3。

  2. 分堆:把剩下的球跟 3 比,分成三堆:

    • 大球堆 (Big) :[6, 5, 4] (它们都比 3 大)

    • 等球堆 (Equal) :[3]

    • 小球堆 (Small) :[2, 1] (它们都比 3 小)

2. 关键逻辑:你在哪一堆?

我们要找的是全场第 2 大。现在看这三堆:

  • 第一堆 (Big) 里面有 3 个球。

  • 既然这 3 个球是全场最大的,那么第 2 大的球一定藏在这 3 个球里面!

结论 :剩下那两堆([3] 和 [2, 1])我们可以直接扔掉了!它们绝对不可能是第 2 大。

下一步 :我们只需要在 [6, 5, 4] 这 3 个球里,继续找第 2 大。


3. 运行步骤(动图式拆解)

我们继续刚才的步骤,目标:在 [6, 5, 4] 中找第 2 大。

  1. 再随便抓一个 :比如抓到了 5。

  2. 分堆:

    • 大球堆 (Big) :[6] (比 5 大)

    • 等球堆 (Equal) :[5] (等于 5)

    • 小球堆 (Small) :[4] (比 5 小)

  3. 判断位置:

    • 全场最大的是 [6](只有 1 个球)。

    • 我们要找第 2 大。

    • 第 1 大是 6,那么第 2 大不就是紧随其后的"等球堆"里的 5 吗?

成交!找到结果:5。


4. 为什么代码里有 k - (nums.size() - small.size())?

这是最难懂的地方。想象一下:如果你要找全场第 5 大:

  1. 你发现 Big 堆(大球)只有 2 个,Equal 堆(等球)只有 1 个。

  2. 前 3 名都在这两堆里,那第 5 大肯定在 Small 堆(小球)里。

  3. 但是 ,当你把大球和等球扔掉,只盯着小球堆看时,你不能再找"第 5 大"了,因为最强的 3 个已经被你扔了,你现在只需要找小球堆里的第 2 大(5 - 3 = 2)。


5. 总结:它为什么快?

  • 排序 (QuickSort):每一堆都要管,左边排完排右边。

  • 快速选择 (QuickSelect):只管有目标的那一堆,另一堆直接消失。

这就好比你在教学楼找人:

  • 排序:把全校学生按身高排好队,再数第 2 个。

  • 快速选择:问一句"身高 1米8 以上的在几楼?"。听说在 3 楼,你就直接上 3 楼,1 楼和 2 楼的人你连看都不看。

相关推荐
UIU1144 小时前
P1233 [ICPC 2001 Taejon R] 木棍加工
数据结构·c++·算法·动态规划
论文复现现场4 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
ZhengEnCi4 小时前
用 AI 写代码为什么拼到最后还是拼人:算力瓶颈与领域专家提示词的本质差异
算法·架构
秋天的怀念_David5 小时前
第二十一夜 · 数字黑洞与西西弗斯的惩罚
java·程序人生·算法
碧海银沙音频科技研究院6 小时前
在PC电脑上用Whisper→Marian算法实现离线语音翻译功能
算法·whisper
niyongsheng6 小时前
3D 装箱算法工程化:数据结构、放置启发式与支撑规则
数据结构·算法·rust
叉烧滑蛋9 小时前
B站台湾大学郭彦甫|MATLAB 学习笔记|04 变量与档案存取
数据结构
j7~9 小时前
【穷举、深搜与回溯算法】一棵决策树讲透全排列与子集
数据结构·c++·算法·leetcode·回溯·递归
傅科摆 _ py10 小时前
DP,贪心,堆和栈,二分力扣复习
开发语言·python·算法
Shaoshing11 小时前
LeetCode100 42.接雨水
java·算法·leetcode