快速排序(标杆劈叉法)超详细深度解析
快速排序是实践中综合性能最强的通用排序算法被称为「排序算法之王」核心基于分治思想 分区操作通过递归不断缩小问题规模在绝大多数场景下效率远超其他同复杂度排序算法。一、核心思想与分治逻辑快速排序的本质是「基准定位 分而治之」从数组中选一个元素作为基准pivot执行分区操作把所有小于基准的元素移到基准左边所有大于基准的元素移到基准右边操作完成后基准直接落在它最终排序的正确位置上递归对基准左侧、右侧的两个子数组重复上述操作直到子数组长度为 1天然有序整个过程像用标杆把数组一次次 “劈开” 成两半因此也被形象称为「标杆劈叉法」。二、核心流程分步详解快速排序的核心是分区Partition分区的实现方式直接决定代码写法和效率行业内有两种经典分区方案霍尔分区双指针两端法和洛穆托分区单指针遍历法。2.1 方案一霍尔分区Hoare Partition双指针法这是快排发明者霍尔提出的原始方案也是最常用、交换次数最少的实现采用左右双指针从两端向中间逼近。分区步骤演示以数组[6, 1, 2, 7, 9, 3, 4, 5, 10, 8]为例选左端点 6 作为基准完整分区过程如下初始化左指针left指向数组首元素右指针right指向数组尾元素保存基准值pivot 6右指针左移从右往左找第一个小于基准的数从末尾 8 开始86、106、56 → 右指针停在索引 7元素 5将右指针的值 5 赋值给左指针位置数组变为[5, 1, 2, 7, 9, 3, 4, 5, 10, 8]左指针右移从左往右找第一个大于基准的数从开头 5 开始56、16、26、76 → 左指针停在索引 3元素 7将左指针的值 7 赋值给右指针位置数组变为[5, 1, 2, 7, 9, 3, 4, 7, 10, 8]重复上述两步左右指针不断向中间收缩右指针继续左移找到 4索引 6赋值给左指针位置 →[5, 1, 2, 4, 9, 3, 4, 7, 10, 8]左指针继续右移找到 9索引 4赋值给右指针位置 →[5, 1, 2, 4, 9, 3, 9, 7, 10, 8]右指针继续左移找到 3索引 5赋值给左指针位置 →[5, 1, 2, 4, 3, 3, 9, 7, 10, 8]当左右指针相遇时left right分区结束将基准值 6 放到指针相遇的位置最终分区结果[5, 1, 2, 4, 3, 6, 9, 7, 10, 8]基准 6 已经处于最终排序的正确位置左侧全小于 6右侧全大于 6分区代码实现cpp运行// 霍尔分区返回基准元素的最终下标 int partition(vectorint nums, int left, int right) { int pivot nums[left]; // 选左端点作为基准 while (left right) { // 右指针从右往左找第一个小于基准的元素 while (left right nums[right] pivot) { right--; } nums[left] nums[right]; // 移到左指针位置 // 左指针从左往右找第一个大于基准的元素 while (left right nums[left] pivot) { left; } nums[right] nums[left]; // 移到右指针位置 } nums[left] pivot; // 基准归位 return left; // 返回基准的最终位置 }注意内层循环的判断条件必须带等号/否则遇到和基准相等的元素会陷入死循环。2.2 方案二洛穆托分区Lomuto Partition单指针法实现更简洁的分区方案选右端点作为基准用单指针遍历数组维护「小于基准的区域边界」遍历完成后将基准交换到分界处。分区步骤演示同样以数组[6, 1, 2, 7, 9, 3, 4, 5, 10, 8]为例选右端点 8 作为基准初始化i -1表示「小于基准的区域」的右边界j 从 0 开始遍历j 遍历到小于等于基准的元素时i 右移一位交换 i 和 j 位置的元素把元素纳入小于区遍历结束后交换 i1 位置和基准位置基准归位最终基准 8 落在索引 7左侧全小于等于 8右侧全大于 8。分区代码实现cpp运行int partition(vectorint nums, int left, int right) { int pivot nums[right]; // 选右端点作为基准 int i left - 1; // 小于基准的区域的右边界 for (int j left; j right; j) { if (nums[j] pivot) { i; // 扩大小于区 swap(nums[i], nums[j]); } } swap(nums[i 1], nums[right]); // 基准归位 return i 1; }特点代码简单易写但交换次数比霍尔分区多整体效率略低。三、完整递归实现基础版分区完成后对基准左右两个子数组递归执行相同逻辑直到子数组长度 ≤ 1。cpp运行#include vector using namespace std; // 霍尔分区函数复用上面的实现 int partition(vectorint nums, int left, int right) { int pivot nums[left]; while (left right) { while (left right nums[right] pivot) right--; nums[left] nums[right]; while (left right nums[left] pivot) left; nums[right] nums[left]; } nums[left] pivot; return left; } // 快速排序主递归函数 void quickSort(vectorint nums, int left, int right) { // 递归终止条件区间为空或只有一个元素天然有序 if (left right) { return; } // 1. 分区得到基准的最终位置 int pivotPos partition(nums, left, right); // 2. 递归排序基准左侧区间 quickSort(nums, left, pivotPos - 1); // 3. 递归排序基准右侧区间 quickSort(nums, pivotPos 1, right); } // 对外调用接口 void quickSort(vectorint nums) { if (nums.empty()) return; quickSort(nums, 0, nums.size() - 1); }四、复杂度深度分析4.1 时间复杂度快速排序的时间复杂度取决于分区的均匀程度也就是基准选择的好坏。最好情况O (nlogn)每次分区都把数组均匀切成两半递归深度为 log₂n每层分区总操作量为 O (n)由主定理可推导出时间复杂度 O (nlogn)。平均情况O (nlogn)绝大多数场景下基准选择不会极端不均匀平均时间复杂度稳定在 O (nlogn)且常数因子远小于归并排序、堆排序实际运行速度最快。最坏情况O (n²)当数组已经完全有序 / 逆序且选端点作为基准时每次分区只能分出 1 个元素和 n-1 个元素递归深度为 n总操作量退化为 O (n²)。这也是基础快排的最大缺陷可以通过优化基准选择彻底避免。4.2 空间复杂度主要开销是递归调用栈最好 / 平均情况递归深度为 O (logn)最坏情况为 O (n)分区操作是原地排序不需要额外数组空间优化后尾递归优化可将最坏栈空间也控制在 O (logn)五、稳定性分析快速排序是不稳定排序。原因分区过程中元素会发生远距离交换可能打乱值相等元素的原始相对顺序。举例数组[2, 1, 1]两个 1 标记先后顺序选第一个 2 作为基准分区后结果为[1, 1, 2]原本在后面的 1 被移到了前面两个 1 的相对顺序被破坏六、经典优化方案进阶重点基础版快排在极端场景下存在性能缺陷工业级实现都会做多层优化以下是最核心的 5 种优化手段。优化 1随机选择基准解决问题避免有序数组下退化为 O (n²)。原理分区前随机选一个元素和左端点交换再以左端点为基准。彻底打破 “有序数组选端点必最坏” 的局面让 O (n²) 的概率趋近于 0。cpp运行int partition(vectorint nums, int left, int right) { // 随机选一个位置和左端点交换 int randIdx left rand() % (right - left 1); swap(nums[left], nums[randIdx]); int pivot nums[left]; // ... 后续分区逻辑不变 }优化 2三数取中法选基准解决问题比随机基准更稳定避免选到极端值作为基准。原理取区间左端点、中间点、右端点三个元素选择值为中位数的元素作为基准保证基准不会是最大 / 最小值分区更均匀。cpp运行int getMid(vectorint nums, int left, int right) { int mid left (right - left) / 2; // 让 nums[left] 成为三个数的中位数 if (nums[left] nums[right]) swap(nums[left], nums[right]); if (nums[mid] nums[right]) swap(nums[mid], nums[right]); if (nums[mid] nums[left]) swap(nums[left], nums[mid]); return nums[left]; // 此时左端点就是中位数 }优化 3小区间改用插入排序解决问题小数组下递归开销大插入排序常数因子更小、速度更快。原理当子数组长度小于阈值通常取 10~15时不再递归快排改用插入排序同时减少递归深度。cpp运行void quickSort(vectorint nums, int left, int right) { // 小区间用插入排序替代递归 if (right - left 1 10) { insertionSort(nums, left, right); return; } int pivotPos partition(nums, left, right); quickSort(nums, left, pivotPos - 1); quickSort(nums, pivotPos 1, right); }优化 4三路快速排序荷兰国旗优化解决问题数组中存在大量重复元素时普通快排会重复分区相等元素效率低下。原理将数组分成三部分小于基准、等于基准、大于基准等于基准的元素已经归位后续只递归小于和大于的区间大幅减少重复分区。cpp运行void quickSort3Way(vectorint nums, int left, int right) { if (left right) return; int pivot nums[left rand() % (right - left 1)]; int lt left - 1; // 小于区右边界 int gt right 1; // 大于区左边界 int i left; // 当前遍历指针 while (i gt) { if (nums[i] pivot) { swap(nums[lt], nums[i]); // 放入小于区 } else if (nums[i] pivot) { swap(nums[--gt], nums[i]); // 放入大于区i不移动新换过来的元素还没判断 } else { i; // 等于基准直接跳过 } } // 只递归小于和大于的区间等于区已经有序 quickSort3Way(nums, left, lt); quickSort3Way(nums, gt, right); }优化 5尾递归优化解决问题减少递归栈深度避免最坏情况栈溢出。原理每次只递归较短的那一半区间另一半用循环处理保证递归深度不超过 logn。七、常见易错点与面试考点边界死循环内层循环必须带等号且必须先移动右指针再移动左指针选左端点为基准时否则会出现越界或死循环递归范围基准已经归位递归时必须排除基准即[left, pivot-1]和[pivot1, right]溢出问题计算中间下标时用left (right - left) / 2不要用(left right) / 2避免数值溢出TopK 问题快速排序的分区思想是解决 TopK 问题的最优方案无需完全排序即可找到第 K 大 / 小的元素平均时间复杂度 O (n)非递归实现可以用栈保存每一层的左右区间模拟递归过程避免栈溢出谢谢

相关新闻

交换机配置总出错?彻底搞懂Access、Trunk、Hybrid三种端口模式

交换机配置总出错?彻底搞懂Access、Trunk、Hybrid三种端口模式

在企业网络建设和运维工作中,VLAN几乎是绕不开的话题。无论是办公网络、数据中心,还是园区网络,VLAN都承担着网络隔离、业务划分和安全控制的重要职责。而当网络工程师开始配置VLAN时,最先接触到的往往就是交换机端口模式:Access、Trunk和Hybrid。 很多刚接触交换机配置的…

2026/10/16 16:31:19 阅读更多 →
TraceX的事件追踪配置与数据导出

TraceX的事件追踪配置与数据导出

引言 在上一篇文章中,我们介绍了TraceX的基本概念和使用方法。TraceX作为ThreadX的可视化分析工具,能够帮助开发者深入了解系统运行状态。然而,要充分发挥TraceX的作用,需要正确配置事件追踪和数据导出功能。 本文将详细介绍TraceX的事件追踪配置方法和数据导出机制,包括…

2026/10/17 0:02:55 阅读更多 →
使用TraceX进行系统行为可视化分析

使用TraceX进行系统行为可视化分析

引言 在嵌入式实时系统开发过程中,系统行为分析是一个非常重要的环节。由于嵌入式系统资源有限,传统的调试方法(如printf输出)往往无法全面地反映系统运行状态。ThreadX提供了一款强大的可视化分析工具——TraceX,它可以帮助开发者深入了解系统的运行状态,包括线程调度、…

2026/10/17 8:11:08 阅读更多 →

最新新闻

diagram-design:构建可维护图表设计系统的核心方法论

diagram-design:构建可维护图表设计系统的核心方法论

1. 从一张草图到一套系统:diagram-design 到底在解决什么问题第一次看到 diagram-design 这个词,很多人会下意识觉得它只是“画图”的另一种说法。但真正在项目里被图表折磨过的人会明白,画图本身从来不是难点,难的是让图在三个月…

2026/10/19 6:03:37 阅读更多 →
artcraft框架:从创意到实物的完整工作流拆解

artcraft框架:从创意到实物的完整工作流拆解

1. 从“artcraft”这个名字说起:它到底想解决什么问题第一次看到“artcraft”这个标题,我脑子里蹦出来的第一反应是:这大概率不是一个单纯的绘画工具,也不是一个纯粹的手工教程合集。把“art”和“craft”拼在一起,本身…

2026/10/19 6:03:37 阅读更多 →
工业指针仪表识别系统:抗油污低光倾斜的Python全流程方案

工业指针仪表识别系统:抗油污低光倾斜的Python全流程方案

简介:本资源是一份面向工业智能化巡检场景的Python图像识别系统设计论文,适用于自动化、仪器仪表、计算机视觉方向的本科生、研究生及现场工程师,解决指针式模拟仪表人工读数效率低、无人巡检落地难的核心痛点。论文提出基于DeepLabV3语义分割…

2026/10/19 6:03:37 阅读更多 →
从防御规避到加密流量检测:AES-256-GCM下的安全运营实战

从防御规避到加密流量检测:AES-256-GCM下的安全运营实战

之前和一些同行聊“免杀”这个话题,大家的反应往往两极分化:有人觉得它神秘,有人觉得它就是恶意软件对抗杀毒的手段,没什么好聊的。我自己的看法其实不同——在安全运营和检测工程这个视角里,“免杀”并不是一个孤立的…

2026/10/19 6:03:37 阅读更多 →
昆仑通态触摸屏与台达温控器Modbus RTU联调:寄存器映射与报警批量配置

昆仑通态触摸屏与台达温控器Modbus RTU联调:寄存器映射与报警批量配置

1. 联调前必须捋清楚:接线、通信参数与485总线的坑1.1 最小系统怎么搭:从硬件接线开始做项目这些年,最怕的不是工艺逻辑复杂,而是上位机和仪表的“通信语言”对不上。前阵子接手一套加热设备改造,现场是25台台达DT330温…

2026/10/19 6:03:37 阅读更多 →
React 18并发渲染实战:自动批处理、startTransition与Suspense优化搜索筛选页

React 18并发渲染实战:自动批处理、startTransition与Suspense优化搜索筛选页

前段时间我接手一个搜索筛选页的重构,数据量不算夸张,两千多条记录、六七个筛选条件,但旧实现里每敲一个字,键盘都明显发沉。页面后来变流畅,靠的就是React 18的并发渲染能力,重点落在Suspense、Transition…

2026/10/19 6:02:37 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →