Cosmos 项目中的 QuickSelect 选择算法:在无序数组中高效查找第 k 小元素
教程示例工程【免费下载链接】cosmosWorlds largest Contributor driven code dataset | Used in Quark Search Engine, OpenGenus IQ, OpenGenus Visual Project项目地址https://gitcode.com/gh_mirrors/co/cosmos点击查看免费下载导读QuickSelect快速选择是一种用于在无序列表中查找第 k 小元素的选择算法它与快速排序算法同源可以看作只递归一半的快速排序。本文以 selection_algorithms 目录 的官方说明为骨架结合 Cosmos 仓库中 C、Python、Java、Go、Kotlin、Swift、C、Haskell 等多语言实现系统讲解 QuickSelect 的算法原理、分区过程、复杂度分析、典型应用场景以及如何用中位数中位数Median of Medians把最坏时间复杂度从 O(n²) 稳定到 O(n)。读完本文你将掌握在任意语言中实现并正确使用选择算法的完整能力。1. 什么是 QuickSelect与快速排序的关系QuickSelect 是一种选择算法用于在无序列表中查找第 k 小的元素。它与快速排序算法密切相关。 —— code/selection_algorithms/src/README.md这两者的核心区别体现在递归策略上快速排序选定 pivot 后对左右两侧都进行递归最终使整个数组有序QuickSelect选定 pivot 并完成分区后只递归包含目标元素的那一侧另一侧直接丢弃。仓库中的 C 实现 在注释中明确写出了这一设计要点This algorithm is similar to quicksort because it chooses an element as a pivot and partitions the data into two based on the pivot. However, unlike quicksort, quickselect only recurses into one side - the side with the element it is searching for.正是因为每轮只处理一侧QuickSelect 的平均代价才能从快速排序的 O(n log n) 降到 O(n)。1.1 第 k 小与第 k 大的一体两面quick_select.java 的开头注释给出了一个实用的等价关系Find kth largest element is equivalent to find (n - k)th smallest element in array. It is worth mentioning that (n - k) is the real index (start from 0) of an element.也就是说在长度为 n 的数组中求第 k 大元素等价于求第 n - k 小下标从 0 计数的元素。Java 实现正是利用这一点把findKthLargest(nums, k)转换为寻找下标为nums.length - k的第 k 小元素。这一个等价变换让 QuickSelect 可以统一解决第 k 小和第 k 大两类问题。2. 算法核心随机化分区PartitionQuickSelect 的核心步骤与快速排序的 Lomuto 分区一致仓库中 C 版本 的实现如下int partition(vectorint v, int left, int right, int pivotIndex) { int pivotValue v[pivotIndex]; vSwap(v, pivotIndex, right); // 1. 把 pivot 移到末尾 int storeIndex left; for (int i left; i right; i) // 2. 小于 pivot 的元素依次前移 if (v[i] pivotValue) { vSwap(v, storeIndex, i); storeIndex; } vSwap(v, right, storeIndex); // 3. 把 pivot 放回最终位置 return storeIndex; // 返回 pivot 的最终下标 }整个分区过程分为三步暂存 pivot 值并将其与区间右端元素交换避免在遍历中干扰比较一趟扫描维护storeIndex指针把所有小于 pivot 的元素交换到区间左端扫描完成后storeIndex之前的元素全部 pivot把 pivot 放回storeIndex位置此时 pivot 已处于它在有序数组中的最终位置左边全小、右边全大并返回该位置。在 Python 版本 中随机选择的 pivot 被首先交换到列表开头然后扫描剩余元素完成同样的就地分区Go 版本 则与 C 一致采用pivot 移末尾策略。三种写法本质等价都是 O(right - left) 时间、O(1) 额外空间的就地分区。2.1 随机化 pivot 的选择为了让平均复杂度稳定在 O(n)各实现都使用随机 pivotCint pivotIndex left floor(rand() % (right - left 1));quickselect.cppPythonpivot_index random.randint(l, r)quickselect.pyGopivotIndex : rand.Intn(right)quickselect.goKotlinleft Math.floor((rand.nextInt(MAX) % (right - left 1)).toDouble()).toInt()quick_select.ktSwiftrandom(min: low, max: high)基于arc4random_uniformquick_select.swift随机化 pivot 的意义在于它使算法对几乎有序、几乎逆序等退化输入不再敏感——即使每次分区都极不平衡这种事件发生的概率也随规模指数级降低从而在概率意义上保证了 O(n) 的平均行为。3. 递归选择过程只进入一侧完成分区拿到 pivot 下标后QuickSelect 只需三路判断即可定位答案。以 C 实现 为例int select(vectorint v, int left, int right, int k) { if (left right) return v[k]; // Select a random pivot within left and right int pivotIndex left floor(rand() % (right - left 1)); pivotIndex partition(v, left, right, pivotIndex); if (k pivotIndex) return v[k]; // 1. 命中pivot 就是第 k 小元素 else if (k pivotIndex) return select(v, left, pivotIndex - 1, k); // 2. 目标在左半区 else return select(v, pivotIndex 1, right, k); // 3. 目标在右半区 }递归的终止条件与分支逻辑为终止当区间收缩到left right只剩一个候选元素时该元素必为答案命中若k pivotIndex说明 pivot 恰好落在目标位置直接返回左递归若k pivotIndex目标在左半区[left, pivotIndex - 1]右半区整体丢弃右递归若k pivotIndex目标在右半区[pivotIndex 1, right]左半区整体丢弃。Python 实现 在进入递归前还做了两项输入校验空列表返回None、item_index越界抛出IndexErrorquickselect.py这是工程化实现中值得借鉴的健壮性细节。另外 Go 版本 与 Kotlin 版本 的递归逻辑与 C 完全同构其中 Kotlin 使用了tailrec尾递归优化避免深度递归时的栈开销。4. 复杂度分析平均 O(n)最坏 O(n²)原文档给出的复杂度结论是O(n)最坏情况 O(n²)—— code/selection_algorithms/src/README.md这一结论的推导过程如下4.1 平均情况 O(n)假设每次分区大致平衡即 pivot 位于区间中位附近。规模为 n 时第一轮分区开销为 cn之后只对约 n/2 的一侧递归于是有递推式T(n) T(n/2) cn由主定理或等比数列求和可知 T(n) O(n)。直观理解n n/2 n/4 ... 2n所以总工作量是输入规模的常数倍。随机化 pivot 保证了大致平衡以高概率成立这正是各语言实现都采用随机化的原因。4.2 最坏情况 O(n²)如果 pivot 每次都选到当前区间的最小值或最大值则每轮只排除一个元素递推式退化为T(n) T(n-1) cn累加得 T(n) O(n²)这与快速排序最坏情形同源。需要强调的是选择固定 pivot如始终取第一个元素时对已有序输入必然触发最坏情况而随机 pivot 只是让这种情况的概率极低并不能从理论上消除。若需要严格保证最坏情况 O(n)需采用下一节的中位数中位数算法。4.3 空间复杂度QuickSelect 是就地算法分区只使用常数个临时变量递归深度平均为 O(log n)最坏为 O(n)。Kotlin 版使用tailrec优化后理论上可将递归开销摊薄到常数级quick_select.kt。5. 确定性改进Median of Medians中位数中位数针对随机化无法消除最坏情况的缺陷仓库在 median_of_medians 子目录 提供了确定性选择算法其 C 实现注释开宗明义The median of medians algorithm. Deterministic select algorithm that executes in O(n) in the worst case. —— median_of_medians.c5.1 算法步骤以 Python 实现 为参照核心流程为分组把数组按每 5 个一组切分chunks [A[i : i 5] for i in range(0, len(A), 5)]求各组中位数对每个小组递归调用select(chunk, len(chunk) // 2)取中位数递归求中位数的中位数把上一步得到的 medians 列表再次求中位数得到 pivot 候选medianOfMedians按 pivot 分区将数组分成lowerPartition pivot与upperPartition pivot两部分三路判断若i len(lowerPartition)则 pivot 即答案若i更大则递归右半区并把索引减去左侧长度和 pivot 本身否则递归左半区。C 实现 getMedianOfMedians 用一个循环完成分组 组内插入排序insertionSortmedian_of_medians.c 中位数前置随后递归计算中位数的中位数Haskell 版本则借助chunksOf 5与map median以纯函数风格表达同一流程median_of_medians.hs。5.2 为什么组大小取 5分组大小取 5 是数学推导的结果每组 5 个元素、组数为 n/5中位数的中位数能保证至少有约 3n/10 个元素小于 pivot、约 3n/10 个元素大于 pivot从而每次分区后问题规模最多收缩到约 7n/10。由此得到递推式T(n) T(n/5) T(7n/10) O(n)解得 T(n) O(n)即最坏情况线性时间。若组大小取 3 则无法保证线性界这正是实现中把分组大小硬编码为 5 的原因C 版定义为#define MEDIAN_GROUPS_SIZE 5见 median_of_medians.c。5.3 权衡优点最坏情况复杂度严格为 O(n)无随机性适合对最坏延迟敏感的场景缺点常数因子较大每轮需要求中位数的中位数、多次递归实际运行通常慢于随机化 QuickSelect。因此实践中默认选择仍是随机化 QuickSelect中位数中位数更多作为理论上的确定性上界手段。6. 快速排序还是 QuickSelect排序后取下标的选择仓库中的 Swift 实现 给出了一个最朴素但正确的对照方案public func kthLargest(_ a: [Int], _ k: Int) - Int? { let len a.count if k 0 k len { let sorted a.sorted() return sorted[len - k] } else { return nil } }当数据量很小时直接全量排序再按下标取值O(n log n)实现最简单、不易出错而当 n 较大、或需要频繁求第 k 小元素时QuickSelect 的 O(n) 平均复杂度优势显著。仓库同时提供了第三条路径quickselect_stl.cpp 借助 C 标准库std::nth_element一行完成同样的选择nth_element(v.begin(), v.begin() k, v.end()); cout v[k] endl;std::nth_element保证第 k 个位置上的元素就是排序后应处的元素左侧全 ≤、右侧全 ≥其平均复杂度为线性内部正是 QuickSelect 类算法的工业级实现。三种方案朴素排序、手写 QuickSelect、标准库 nth_element可在不同场景下互相印证。7. 边界条件与正确性要点综合仓库各语言实现使用 QuickSelect 时有以下边界条件需要特别注意要点说明仓库依据空输入Python 实现对空列表返回Nonequickselect.py索引越界k 不在[0, n-1]时抛出异常/返回 nilquickselect.py、quick_select.swift单元素区间left right时直接返回无需再分区quickselect.cpp递归出口当k pivotIndex时立即返回避免无限递归quickselect.cpp第 k 大 ↔ 第 k 小用n - k下标互换quick_select.java就地分区全程在原数组上交换不申请额外数组各语言partition实现C 主函数示例 用一个 20 个元素的随机序列验证select(v, 0, v.size() - 1, 0)能正确返回最小值即第 0 小元素Kotlin 主函数 则对k 0..9依次调用并打印结果可直接观察算法对每个 k 的输出与排序结果的一致性。8. 典型应用场景在实际工程与算法竞赛中QuickSelect 通常用于以下场景求中位数取k n / 2即可在线性时间内得到中位数这也是 Median of Medians 版实现中median函数的用法median_of_medians.hsTop-K 问题求第 k 大元素后其右侧或左侧元素天然构成 Top-K 集合无需完整排序分位数与统计求 p 分位数、数组的众数候选、成绩排名等异常值检测与过滤先定位中位数或特定分位点再以 O(n) 代价划分数据集数据流/批处理预处理在大规模数据排序前先求出阈值元素做分桶。在这些场景中QuickSelect 相比先排序再取下标能省去大量与目标无关的排序工作这正是选择算法存在的意义。9. 总结维度结论算法本质快速排序的半递归变体只处理包含目标元素的一侧平均复杂度O(n)随机化 pivot 保证最坏复杂度O(n²)固定 pivot 退化输入时触发确定性改进Median of Medians分组大小为 5最坏 O(n)空间复杂度就地分区 O(1) 辅助空间递归栈 O(log n) 平均多语言覆盖C、Python、Java、Go、Kotlin、Swift、C、Haskell 八种实现仓库路径选择算法目录Cosmos 仓库在 selection_algorithms 下完整收录了 QuickSelect 的随机化实现与 Median of Medians 确定性实现并配套 测试目录 与 C 标准库封装。若需进一步探索可对照阅读快速排序的完整实现见 divide_conquer 下的 quick_sort理解两者全递归与半递归的差异即可彻底掌握这一 O(n) 选择利器。赞分享教程示例工程【免费下载链接】cosmosWorlds largest Contributor driven code dataset | Used in Quark Search Engine, OpenGenus IQ, OpenGenus Visual Project项目地址https://gitcode.com/gh_mirrors/co/cosmos点击查看免费下载相关推荐TheAlgorithms/C快速选择算法高效查找无序数组第K小元素的终极指南TheAlgorithms/C快速选择算法高效查找无序数组第K小元素的终极指南 快速选择算法是一种高效的查找无序数组中第K小元素的算法由计算机科学家Tony示例工程Hello 算法Top-k 问题深度剖析——如何用最小堆从无序数组中高效找出最大的 k 个元素Hello 算法Top k 问题深度剖析——如何用最小堆从无序数组中高效找出最大的 k 个元素 Top k 问题Top k Problem是堆heap教程文档示例工程教育LeetCode 230 题解在 BST 中查找第 K 小元素Kth Smallest Integer in a BSTLeetCode 230 题解在 BST 中查找第 K 小元素Kth Smallest Integer in a BST 导读 本题要求在一棵二叉搜索树示例工程教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

C#教学网站源码包:从运行部署到二次开发指南

C#教学网站源码包:从运行部署到二次开发指南

简介:这是一份基于C#的计算机教学网站完整源码,采用ASP.NET WebForm三层架构,运行环境为VS2010与SQL Server 2008,适合.NET初学者、课程设计或毕业设计参考。网站整合了视频上传与浏览、文件上传下载、在线答疑、信息展示、分权限…

2026/9/25 22:08:55 阅读更多 →
从零构建类 Dify 智能体编排平台:easy-vibe Stage 2 综合项目实战指南

从零构建类 Dify 智能体编排平台:easy-vibe Stage 2 综合项目实战指南

教程文档 【免费下载链接】easy-vibe 从 0 到 1 学会 vibe coding,项目制学习 项目地址: https://gitcode.com/datawhalechina/easy-vibe 点击查看 免费下载 导读 本文围绕 easy-vibe 课程 Stage 2 的综合实战项目——「Custom Dify Agent Platform」展…

2026/9/25 7:29:01 阅读更多 →
razzle-dev-utils 工具集完全指南:从日志、错误美化到 Loader 查找的 Razzle 开发辅助库

razzle-dev-utils 工具集完全指南:从日志、错误美化到 Loader 查找的 Razzle 开发辅助库

前端构建工具前端构建后端 【免费下载链接】razzle ✨ Create server-rendered universal JavaScript applications with no configuration 项目地址: https://gitcode.com/gh_mirrors/ra/razzle 点击查看 免费下载 本指南以 Razzle 仓库中 packages/razzle-dev-ut…

2026/9/23 23:07:30 阅读更多 →

最新新闻

七星卫通技术专业吗

七星卫通技术专业吗

从北斗卫星导航系统完成全球组网,到天通一号卫星移动通信系统建成,国产卫星通信产业从追赶到并跑,从单点突破到体系成型,走过了十余年的攻坚旅程。在这片关乎信息安全、关乎极端场景通信保障的蓝海中,北京七星卫通科技…

2026/9/25 22:58:20 阅读更多 →
太阳能电池板缺陷检测数据集构建与YOLOv8训练避坑指南

太阳能电池板缺陷检测数据集构建与YOLOv8训练避坑指南

简介:太阳能电池板缺陷检测数据集面向计算机视觉研究者与新能源质检开发者,提供2624张300300像素8位灰度图像,覆盖44个太阳能模块的功能性与缺陷电池样本,缺陷包含内在类型(裂纹、断栅、污染等)与外在退化类…

2026/9/25 22:58:20 阅读更多 →
UNSW-NB15网络攻击检测毕设源码实战:从环境配置到部署排坑

UNSW-NB15网络攻击检测毕设源码实战:从环境配置到部署排坑

简介:面向计算机相关专业毕业设计、课程设计与入门实践的机器学习项目资源,围绕 UNSW-NB15 数据集提供网络攻击检测的完整算法实现。数据集涵盖多种现代攻击流量,项目基于经典监督学习思路,集中展示决策树二分类、逻辑回归与 KNN …

2026/9/25 22:58:20 阅读更多 →
OpenClaw-China-Docker微信官方插件接入教程:如何把AI助手装进微信聊天

OpenClaw-China-Docker微信官方插件接入教程:如何把AI助手装进微信聊天

OpenClaw-China-Docker微信官方插件接入教程:如何把AI助手装进微信聊天 【免费下载链接】openclaw-china-docker OpenClaw 的中国IM平台整合Docker版本,预装并配置了飞书、钉钉、QQ机器人、企业微信等主流中国IM软件的插件,让您可以快速部署一…

2026/9/25 22:58:20 阅读更多 →
LDA主题模型关键词提取实战:从分词到gensim调参与避坑指南

LDA主题模型关键词提取实战:从分词到gensim调参与避坑指南

简介:面向文本挖掘与自然语言处理学习者打造的LDA主题建模资源包,聚焦利用潜在狄利克雷分配模型完成关键词与主题词提取,适合需要理解主题模型原理、动手实现文本分析的初学者及研究者,也可应用于新闻聚类、舆情分析与文档主题挖掘…

2026/9/25 22:58:20 阅读更多 →
Nasiko A2A Registry 设计解析:把“Agent 发现“本身做成一个 A2A Agent

Nasiko A2A Registry 设计解析:把“Agent 发现“本身做成一个 A2A Agent

【免费下载链接】nasiko Developer Control Plane for your AI Agents 项目地址: https://gitcode.com/gh_mirrors/na/nasiko 点击查看 免费下载 在 Nasiko(Developer Control Plane for your AI Agents)中,Agent 之间的通信、发…

2026/9/25 22:57:20 阅读更多 →

日新闻

AI元人文:从工具使用到思维重构的深度探索

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:00:41 阅读更多 →
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:00:41 阅读更多 →
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/25 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/25 19:27:14 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/25 11:15:26 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/25 20:29:09 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/25 20:29:43 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/25 20:29:31 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/25 19:27:26 阅读更多 →