news 2026/5/9 6:52:14

快速排序 - 原理、时空分析、优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
快速排序 - 原理、时空分析、优化

过程

快速排序分为三个过程:

  1. 将数列根据划分值m mm划分为两部分;
  2. 递归到两个子序列中分别进行快速排序;
  3. 不用合并,因为此时数列已经完全有序。

具体来说,第一步要是要把数列分成两个部分,然后保证前一个子数列中的数都小于后一个子数列中的数。对于最优情况,每一次选择的分界值都是序列的中位数。

快速排序归并排序虽然都是划分区间,但是归并排序直接就可以选取中间位置,但快速排序需要选择的是中间数值,中间位置你是可以直接确定的,而中间数值你是无法确定的。为了保证平均时间复杂度,一般是随机选择一个数m mm来当做两个子数列的分界。

其实,快速排序没有指定应如何具体实现第一步,不论是选择m mm的过程还是划分的过程,都有不止一种实现方法。

第三步中的序列已经分别有序且第一个序列中的数都小于第二个数,所以直接拼接起来就好了。

在实现时,只需维护小于等于m mm的边界l o w lowlow,大于m mm的部分自然就唯一确定了。

朴素快速排序

publicstaticvoidquickSort(intl,intr){if(l>=r)// 只有一个数,或范围不存在return;intx=arr[l];// 固定方式选取划分值intmid=partition(l,r,x);// mid 位置已经固定// 处理剩余区间quickSort(l,mid-1);quickSort(mid+1,r);}// 划分数组 ≤x 放左边,>x 放右边publicstaticintpartition(intl,intr,intx){intlow=l,xi=0;for(inti=l;i<=r;i++){if(arr[i]<=x){swap(low,i);if(arr[low]==x)xi=low;low++;}}swap(xi,low-1);// 确保 ≤x 区域的最后一个数字是 xreturnlow-1;}

这里再贴一个 c 风格的,双指针扫描写法的 partition,常见于数据结构教材中。

intpartition(intlow,inthigh,intx){while(low<high){while(low<high&&arr[high]>=x)high--;swap(low,high);while(low<high&&arr[low]<=x)low++;swap(low,high);}returnlow;}

时间复杂度

快速排序的最优时间复杂度和平均时间复杂度为O ( n log ⁡ n ) O(n\log n)O(nlogn),最坏时间复杂度为O ( n 2 ) O(n^2)O(n2)

对于最优情况,每一次选择的分界值都是序列的中位数,此时算法时间复杂度满足的递推式为T ( n ) = 2 T ( n 2 ) + Θ ( n ) T(n) = 2T\left( \frac{n}{2} \right)+Θ(n)T(n)=2T(2n)+Θ(n),由主定理,T ( n ) = Θ ( n log ⁡ n ) T(n)=Θ(n\log n)T(n)=Θ(nlogn)

对于最坏情况,每一次选择的分界值都是序列的最值,此时算法时间复杂度满足的递推式为T ( n ) = T ( n − 1 ) + Θ ( n ) T(n)=T(n-1)+Θ(n)T(n)=T(n1)+Θ(n),易得T ( n ) = Θ ( n 2 ) T(n)=Θ(n^2)T(n)=Θ(n2)

对于平均情况,每一次选择的分界值可以看作是等概率随机的,设划分值在k kk位置,那么:T ( n ) = 1 n ∑ k = 1 n ( T ( k − 1 ) + T ( n − k ) ) + n = 2 n ∑ k = 1 n T ( k ) + n T(n)=\frac{1}{n}\sum_{k=1}^n(T(k-1)+T(n-k))+n=\frac{2}{n}\sum_{k=1}^nT(k)+nT(n)=n1k=1n(T(k1)+T(nk))+n=n2k=1nT(k)+n
由数学归纳法可证明,其数量级为O ( n log ⁡ n ) O(n\log n)O(nlogn)

在实际中,几乎不可能达到最坏情况,而快速排序的内存访问遵循局部性原理(递归处理相邻子数组、原地分区,连续访问缓存行),所以多数情况下快速排序的表现大幅优于堆排序等其他复杂度为O ( n log ⁡ n ) O(n\log n)O(nlogn)的排序算法。

就空间复杂度而言,主要是递归造成的栈空间的使用,最好情况,递归树的深度为log ⁡ 2 n \log_{2}nlog2n,其空间复杂度也就为O ( log ⁡ n ) O(\log n)O(logn),最坏情况,需要进行n − 1 n-1n1递归调用,其空间复杂度为O ( n ) O(n)O(n),平均情况,空间复杂度也为O ( log ⁡ n ) O(\log n)O(logn)

优化

  • 当序列较短时,直接使用插入排序的效率更高;
  • 尾递归(迭代)可以缩减堆栈深度,提高整体性能。

我们还有两条主路径去优化:划分值m mm的选取划分的过程

划分值m mm

如果你用朴素方法中的固定方式选取划分值m mm,比如总选数列第一个或最后一个元素,毒瘤数据能够把朴素的快速排序卡成O ( n 2 ) O(n^2)O(n2),比如升序或降序数列。

  • 通过三数取中,即选取第一个、最后一个以及中间的元素中的中位数。

    • 对于非常大的待排序的数列,也有九数取中,不再详述。
  • 通过随机选取,即随机选一个[ l , r ] [l,r][l,r]上的下标对应的值。虽然随机的常数时间比较大,但只有这一下随机,才能在概率上把快速排序的时间复杂度收敛到O ( n log ⁡ n ) O(n\log n)O(nlogn)

划分过程

朴素方法中,每次划分的过程都只能确定一个位置的值,即p a r t i t i o n partitionpartition返回的位置。

三路快速排序

是快速排序和基数排序的混合。思想基于 荷兰国旗问题。

每次划分过程,将待排数列划分为三个部分:小于m mm、等于m mm以及大于m mm。在处理含有多个重复值的数组时,效率远高于朴素快速排序。其最佳时间复杂度为O ( n ) O(n)O(n)

在实现时,只需维护小于m mm的边界,和大于m mm的边界,等于的部分自然就唯一确定了。

荷兰国旗优化版随机快速排序

publicstaticintfirst,last;publicstaticvoidquickSort(intl,intr){if(l>=r)return;// [l, r+1) -> l + [0, r-l+1)intx=arr[l+(int)(Math.random()*(r-l+1))];partition(l,r,x);intleft=first;intright=last;// 记录一下 lastquickSort(l,left-1);// 经过左半边快排,last 可能被更改quickSort(right+1,r);}publicstaticvoidpartition(intl,intr,intx){first=l;last=r;inti=l;while(l<=last){if(arr[i]==x)i++;elseif(arr[i]<x)swap(first++,i++);elseswap(last--,i);}}
内省排序

是快速排序和堆排序的结合。保证了最差时间复杂度为O ( n log ⁡ n ) O(n\log n)O(nlogn)

内省排序将快速排序的最大递归深度限制为⌊ log ⁡ n ⌋ \lfloor \log n \rfloorlogn,超过限制时就转换为堆排序。这样既保留了快速排序内存访问的局部性,又可以防止快速排序在某些情况下性能退化为O ( n 2 ) O(n^2)O(n2)

SGI C++ STL 的stl_algo.hsort()函数的实现采用了内省排序算法。

习题

洛谷 P1177【模板】快速排序 模板
力扣 912. 排序数组 模板

#atom

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/4 17:36:36

大数据诊断性分析:从入门到精通的完整指南

大数据诊断性分析&#xff1a;从入门到精通的完整指南 一、引言&#xff1a;为什么你做了一堆报表&#xff0c;却还是找不到问题的根因&#xff1f; 你有没有过这样的经历&#xff1f; 月底盯着复购率下降20%的报表抓耳挠腮&#xff0c;翻了几十张用户行为折线图&#xff0c;…

作者头像 李华
网站建设 2026/5/2 20:40:45

ANSYS有限元分析全流程深度拆解——从入门到精通的技巧与避坑指南

在科研验证、产品优化、工程设计等场景中&#xff0c;ANSYS有限元分析已成为“用数字模拟现实”的核心工具。然而&#xff0c;对于刚接触该技术的从业者而言&#xff0c;常陷入“流程混乱、技巧缺失、结果失真”的困境——要么因几何简化不当导致计算偏差&#xff0c;要么因网格…

作者头像 李华
网站建设 2026/5/1 16:59:17

Node.js用readableLength轻松控流

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 Node.js流控新境界&#xff1a;利用readableLength实现高效背压管理目录Node.js流控新境界&#xff1a;利用readableLength实现高…

作者头像 李华
网站建设 2026/5/1 8:56:22

Hive与Doris整合:MPP引擎加速大数据分析

Hive与Doris整合&#xff1a;MPP引擎加速大数据分析关键词&#xff1a;Hive, Doris, MPP, 大数据分析, 数据整合, 向量化执行, 实时查询加速摘要&#xff1a;本文深入探讨Apache Hive与Apache Doris的整合技术&#xff0c;解析如何通过MPP&#xff08;大规模并行处理&#xff0…

作者头像 李华