跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

算法实战:快速选择解决第 K 大元素与最小 K 个数

快速选择算法基于分治思想,平均时间复杂度 O(N),优于排序的 O(NlogN)。通过 C++ 实现详解 LeetCode 215(第 K 个最大元素)与 LCR 159(最小的 K 个数)。核心在于三路划分策略,根据基准值将数组分为大于、等于、小于三部分,递归定位目标区间。相比堆排序,该方法无需维护额外数据结构,空间效率更高,适合大规模数据筛选场景。

战神发布于 2026/3/29更新于 2026/6/216 浏览
算法实战:快速选择解决第 K 大元素与最小 K 个数

45. 数组中的第 k 个最大元素

题目描述

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

注意这里需要的是排序后第 k 大的元素,而不是第 k 个不同的元素。例如输入 [3,2,1,5,6,4],k=2,输出应为 5。

解法思路:快速选择算法

这道题最直观的做法是排序,时间复杂度为 O(NlogN)。但如果数据量很大,我们其实不需要完全有序,只需要找到第 k 个位置即可。这时候**快速选择算法(Quick Select)**就派上用场了。

它的核心思想来源于快速排序的分区操作(Partition)。在快排中,我们将数组划分为小于基准、等于基准、大于基准三部分。利用这个特性,我们可以判断目标元素究竟落在哪个区间:

  1. 计算区间长度:统计当前分区后,右侧区域(大于基准)和中间区域(等于基准)的元素个数。
  2. 定位目标:
    • 如果右侧区域元素个数 >= k,说明第 k 大就在右边,递归处理右半部分。
    • 如果右侧 + 中间区域个数 >= k,说明基准值就是我们要找的第 k 大元素。
    • 否则,目标在左侧,需要在左边继续查找,同时更新 k 的值(减去已排除的元素数量)。

这种三路划分(Dutch National Flag 问题变种)能有效处理大量重复元素的情况,避免退化。

C++ 参考实现

class Solution {
public:
    // 快速选择主函数,寻找第 k 大的数
    int Top_k(vector<int>& nums, int left, int right, int k) {
        if (left == right) {
            return nums[left];
        }
        
        int l = left - 1, r = right + 1, i = left;
        // 随机选择基准元素,防止最坏情况
        int key = nums[rand() % (right - left + 1) + left];
        
        // 三路划分:[left, l] < key, [l+1, r-1] == key, [r, right] > key
        while (i < r) {
            if (nums[i] > key) {
                swap(nums[i], nums[--r]);
            } else if (nums[i] < key) {
                swap(nums[i++], nums[++l]);
            } else {
                i++;
            }
        }
        
        // 若右边区域元素个数>=k,说明第 k 大的数在右边区域
        if (right - r + 1 >= k) {
            return Top_k(nums, r, right, k);
        }
        // 若右边区域个数<k,但中间加右边区域个数>=k,说明第 k 大的数在中间区域
        else if (right - l >= k) {
            return key;
        }
        // 若中间加右边区域个数<k,说明第 k 大的数在左边区域
        else {
            return Top_k(nums, left, l, k - (right - l));
        }
    }

    int findKthLargest(vector<int>& nums, int k) {
        srand(time(NULL));
        return Top_k(nums, 0, nums.size() - 1, k);
    }
};

46. 最小的 k 个数

题目描述

输入整数数组 stock 表示库存商品,以及整数 cnt 表示需要选取的商品数量。返回数组中最小的 cnt 个数。

例如输入 [3,2,3,1,2,4,5,5,6],cnt=4,输出 [1,2,3,3]。

解法思路

这道题与上一题非常相似,只是方向相反。我们需要找最小的 k 个数,本质上还是利用快速选择的分区思想。

相比堆排序(O(NlogK))或全排序(O(NlogN)),快速选择算法的平均时间复杂度可以逼近 O(N)。虽然代码逻辑稍显巧妙,但在处理大规模数据时性能优势明显。

对比其他方案

  • 全排序:简单直接,但效率较低。
  • 堆排序:适合流式数据,空间占用小,但常数因子较大。
  • 快速选择:原地修改数组,平均速度最快,适合一次性查询场景。

C++ 参考实现

class Solution {
public:
    vector<int> inventoryManagement(vector<int>& stock, int cnt) {
        if (cnt == 0) {
            return {};
        }
        srand(time(NULL));
        // 调用快速选择,将数组前 cnt 个元素调整为最小的 cnt 个
        Top_k(stock, 0, stock.size() - 1, cnt);
        return vector<int>(stock.begin(), stock.begin() + cnt);
    }

    void Top_k(vector<int>& nums, int left, int right, int cnt) {
        if (left == right) {
            return;
        }
        
        int key = nums[rand() % (right - left + 1) + left];
        int l = left - 1, r = right + 1, i = left;
        
        while (i < r) {
            if (nums[i] > key) {
                swap(nums[i], nums[--r]);
            } else if (nums[i] < key) {
                swap(nums[i++], nums[++l]);
            } else {
                i++;
            }
        }
        
        // 根据分区结果决定下一步搜索区间
        if (l - left + 1 >= cnt) {
            return Top_k(nums, left, l, cnt);
        } else if (r - left >= cnt) {
            return;
        } else {
            return Top_k(nums, r, right, cnt - (r - left));
        }
    }
};

总结

这两道题的核心都在于分治策略。通过随机化基准值和三路划分,我们避免了传统快排在重复元素较多时的性能退化。在实际工程中,如果面对海量数据且只需部分有序信息,优先尝试快速选择算法,往往能获得比排序更好的性能表现。

目录

  1. 45. 数组中的第 k 个最大元素
  2. 题目描述
  3. 解法思路:快速选择算法
  4. C++ 参考实现
  5. 46. 最小的 k 个数
  6. 题目描述
  7. 解法思路
  8. 对比其他方案
  9. C++ 参考实现
  10. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • C++ 继承机制详解:概念、规则与菱形继承
  • Spring Cloud 商品服务核心实现:库存、缓存与分布式锁
  • C++ 异常处理机制:异常捕获、自定义异常与实战应用
  • Vivado 工程版本管理实战:Git 配置与重建指南
  • Python 企业级反爬虫核心技巧与风控规避策略
  • 基于 OpenClaw 与 Open WebUI 的企业多部门 AI 平台搭建及 Docker 部署指南
  • LeetCode 202. 快乐数:快慢指针判环解法
  • JavaScript 表单验证禁用与选择框编程详解
  • StarUML 6.3.3 配置指南:汉化、激活与 C++ 扩展搭建
  • Unity+AI 使用自然语言制作小游戏:飞翔的牛马
  • 二分答案专题实战:木材加工与砍树问题详解
  • 单链表核心操作全实现:查找、插入与删除的深度解析
  • Spring Boot 日志体系详解与实战配置指南
  • Seedream 4.0 企业级图像生成能力与应用场景深度解析
  • 二分查找算法实战:x 的平方根与搜索插入位置
  • NestJS 接口响应 Message 编写规范与 API 提示标准化
  • .NET 集成 GoView 低代码可视化大屏实战指南
  • ClawdBot 实战:树莓派 4 运行 OCR/Whisper/vLLM 实现 15 人并发无卡顿
  • 分布式文件系统 HDFS:存储原理详解
  • C++ 模板编程详解:从基础到元编程

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online