快速排序的时间复杂度分析

先说结论:

最坏情况:O(N2)O(N^{2})O(N2)

最好情况和平均情况:O(NlogN)O(NlogN)O(NlogN)

下面开始分析。

假设一个序列共有 N 个元素,基本的快速排序的关系式是:

T(N)=T(i)+T(N−i−1)+cNT(N) = T(i) + T(N -i -1) + cN T(N)=T(i)+T(N−i−1)+cN

其中 i 表示一次划分后,枢纽所在的位置。对一个有 N 个元素的序列排序,其时间可以划分为 3 部分:

1)把枢纽放到合适的位置,即其左边的数都小于等于它,右边的数都大于等于它

2)对左边的部分(共有 iii 个元素)应用快速排序

3)对右边的部分(共有 N−i−1N-i-1N−i−1 个元素)应用快速排序

要想把枢纽元放到合适的位置,需要从两端向中间遍历数组,花费的时间和数组规模成正比例,设为 cNcNcN

于是就有了最上面的公式。

当子序列没有元素或者仅有 1 个元素,处理时间为常数。所以可以认为 :

T(0)=T(1)=1T(0) = T(1)=1T(0)=T(1)=1

最坏情况

枢纽始终是最小元素。此时 i=0i = 0i=0,那么递推关系是:

T(N)=T(0)+T(N−1)+cN,T(N) = T(0) + T(N -1) + cN , T(N)=T(0)+T(N−1)+cN, (N>1)(N>1)(N>1)

忽略掉 T(0)=1T(0)=1T(0)=1 这样的常数项,上面的式子就是:

T(N)=T(N−1)+cNT(N) = T(N -1) + cN T(N)=T(N−1)+cN (1)

反复使用上面的式子,得到

T(N−1)=T(N−2)+c(N−1)T(N-1) = T(N -2) + c(N-1) T(N−1)=T(N−2)+c(N−1) (2)

T(N−2)=T(N−3)+c(N−2)T(N-2) = T(N -3) + c(N-2) T(N−2)=T(N−3)+c(N−2) (3)

最后把 N=2N=2N=2 带入:

T(2)=T(1)+c(2)T(2) = T(1) + c(2) T(2)=T(1)+c(2) (4)

把从 (1) 到 (4)还有省略号,这些式子加起来,消除共同的项,得到:

T(N)=T(1)+c∑i=2Ni=O(N2)T(N) = T(1) + c\sum_{i=2}^{N} i=O(N^{2})T(N)=T(1)+ci=2∑N​i=O(N2)

最好情况

最幸运的是,枢纽恰好位于序列的中间。

为了方便分析,假设数组有奇数个元素,N=2n+1,(n>=1)N = 2n+1,(n>=1)N=2n+1,(n>=1)

所以有

T(N)=T(n)+T(n)+cN,T(N) = T(n) + T(n) + cN , T(N)=T(n)+T(n)+cN, (N>1)(N>1)(N>1)

因为 n=N−12n = \frac{N-1}{2}n=2N−1​,忽略到无关紧要的常数,再简化一下,n=N2n = \frac{N}{2}n=2N​,代入上式

T(N)=T(N/2)+T(N/2)+cN,T(N) = T(N/2) + T(N/2) + cN , T(N)=T(N/2)+T(N/2)+cN, (N>1)(N>1)(N>1)

也就是

T(N)=2T(N/2)+cN,T(N) = 2T(N/2) + cN , T(N)=2T(N/2)+cN, (N>1)(N>1)(N>1)

两边同时除以 NNN

T(N)N=T(N/2)N/2+c\frac{T(N)}{N} =\frac{T(N/2)}{N/2} + cNT(N)​=N/2T(N/2)​+c (1)

反复套用这个式子

T(N/2)N/2=T(N/4)N/4+c\frac{T(N/2)}{N/2} =\frac{T(N/4)}{N/4} + cN/2T(N/2)​=N/4T(N/4)​+c (2)

T(N/4)N/4=T(N/8)N/8+c\frac{T(N/4)}{N/4} =\frac{T(N/8)}{N/8} + cN/4T(N/4)​=N/8T(N/8)​+c (3)

最后把 N=2N=2N=2 带入

T(2)2=T(1)1+c\frac{T(2)}{2} =\frac{T(1)}{1} + c2T(2)​=1T(1)​+c (4)

把从 (1) 到 (4)还有省略号,这些式子加起来,消除共同的项,得到:

T(N)N=T(1)1+cx\frac{T(N)}{N} =\frac{T(1)}{1} + cxNT(N)​=1T(1)​+cx (5)

xxx 表示这些式子有多少个,我们算一下

其个数就是等比数列 2,4,8,16,32,…,N 的个数

根据等比数列的通项公式 an=a1qn−1a_{n} = a_{1}q^{n-1} an​=a1​qn−1

带入 a1=2,an=N,q=2a_{1} =2 , a_{n} = N, q=2a1​=2,an​=N,q=2 得到

N=2nN =2^{n} N=2n

所以 n=log⁡2Nn = \log_{2}Nn=log2​N

所以式子(5)是

T(N)N=T(1)1+clog⁡2N\frac{T(N)}{N} =\frac{T(1)}{1} + c\log_{2}NNT(N)​=1T(1)​+clog2​N (6)

由此得到

T(N)=N+cNlog⁡2NT(N) = N + cN\log_{2}NT(N)=N+cNlog2​N

忽略系数 ccc 和低阶项 NNN,得到

T(N)=O(NlogN)T(N) = O(NlogN)T(N)=O(NlogN)

平均情况

这是最难的部分。

T(N)=T(i)+T(N−i−1)+cNT(N) = T(i) + T(N -i -1) + cN T(N)=T(i)+T(N−i−1)+cN (1)

依然从上面的递推关系式入手。

对于平均情况,很难说每一次划分枢纽元会出现在哪里,也许出现在第 000 个位置,也许出现在第 (N−1)(N-1)(N−1)个位置,但是平均来说,每个位置出现的概率相等,都是 N/1N/1N/1

所以上式的 T(i)T(i)T(i) 要换成 (1/N)∑i=0N−1T(i)(1/N)\sum_{i=0}^{N-1}T(i)(1/N)∑i=0N−1​T(i)

同理,T(N−i−1)T(N -i -1)T(N−i−1) 也可以换成 (1/N)∑i=0N−1T(i)(1/N)\sum_{i=0}^{N-1}T(i)(1/N)∑i=0N−1​T(i)

于是(1)变为

T(N)=(2/N)[∑i=0N−1T(i)]+cN,N>=2T(N) = (2/N)[\sum_{i=0}^{N-1}T(i)]+ cN , N>=2T(N)=(2/N)[i=0∑N−1​T(i)]+cN,N>=2 (2)

两边同时乘以 N

NT(N)=2[∑i=0N−1T(i)]+cN2NT(N) = 2[\sum_{i=0}^{N-1}T(i)]+ cN^2 NT(N)=2[i=0∑N−1​T(i)]+cN2 (3)

用 N−1N-1N−1 来替换 NNN

(N−1)T(N−1)=2[∑i=0N−2T(i)]+c(N−1)2(N-1)T(N-1) = 2[\sum_{i=0}^{N-2}T(i)]+ c(N-1)^2 (N−1)T(N−1)=2[i=0∑N−2​T(i)]+c(N−1)2 (4)

(3) 减去(4) 得到

NT(N)−(N−1)T(N−1)=2T(N−1)+2cN−cNT(N) -(N-1)T(N-1) = 2T(N-1)+2cN-cNT(N)−(N−1)T(N−1)=2T(N−1)+2cN−c (5)

移项

NT(N)=(2+N−1)T(N−1)+2cN−cNT(N) = (2+N-1)T(N-1)+2cN-cNT(N)=(2+N−1)T(N−1)+2cN−c (6)

化简,并忽略常数 c

NT(N)=(N+1)T(N−1)+2cNNT(N) = (N+1)T(N-1)+2cNNT(N)=(N+1)T(N−1)+2cN (7)

两边同时除以 N(N+1)N(N+1)N(N+1)

T(N)N+1=T(N−1)N+2cN+1\frac{T(N)}{N+1} =\frac{T(N-1)}{N} + \frac{2c}{N+1}N+1T(N)​=NT(N−1)​+N+12c​ (8) 现在可以进行叠缩,不断套用式子(8)

T(N−1)N=T(N−2)N−1+2cN\frac{T(N-1)}{N} =\frac{T(N-2)}{N-1} + \frac{2c}{N}NT(N−1)​=N−1T(N−2)​+N2c​ (9)

T(N−2)N−1=T(N−3)N−2+2cN−1\frac{T(N-2)}{N-1} =\frac{T(N-3)}{N-2} + \frac{2c}{N-1}N−1T(N−2)​=N−2T(N−3)​+N−12c​ (10)

把 N=2N=2N=2 带入 (8)

T(2)3=T(1)2+2c3\frac{T(2)}{3} =\frac{T(1)}{2} + \frac{2c}{3}3T(2)​=2T(1)​+32c​ (11)

把 (8,9,10,11) 和省略号的式子加起来

T(N)N+1=T(1)2+2c∑i=3N+1(1/i)\frac{T(N)}{N+1} =\frac{T(1)}{2} + 2c\sum_{i=3}^{N+1}(1/i)N+1T(N)​=2T(1)​+2ci=3∑N+1​(1/i) (12)

忽略常数 T(1)2\frac{T(1)}{2}2T(1)​ 和系数 2c,我们研究一下 ∑i=3N+1(1/i)\sum_{i=3}^{N+1}(1/i)∑i=3N+1​(1/i)

这个式子就是

13+14+15+...+1N+1\frac{1}{3} + \frac{1}{4} + \frac{1}{5}+...+\frac{1}{N+1}31​+41​+51​+...+N+11​ (13)

当 n 很大时

1+12+13+...+1n=ln(n)+γ1 + \frac{1}{2} + \frac{1}{3} + ...+\frac{1}{n} = ln(n)+ \gamma1+21​+31​+...+n1​=ln(n)+γ (14)

其中的 γ\gammaγ 是欧拉常数,近似值是 0.57721566490153286060651209,目前还不知道它是有理数还是无理数。

对比(14)和(13),当(13)中的 NNN 很大时,它的和近似等于

ln(N+1)+γ−3/2ln(N+1)+ \gamma - 3/2ln(N+1)+γ−3/2

结合式子 (12)得出:

T(N)N+1=O(logN)\frac{T(N)}{N+1} = O(logN)N+1T(N)​=O(logN) (12)

从而,

T(N)=O(NlogN)T(N) = O(NlogN)T(N)=O(NlogN)


参考资料:

《数据结构与算法分析——C语言描述》(机械工业出版社)

快速排序的时间复杂度分析相关推荐

  1. 快速排序归并排序—时间复杂度分析

    引言: 大家好,我是小星星,今天要梳理的知识点是--快速排序和归并排序时间复杂度分析. 目录 一.快排时间复杂度分析 二.归并排序时间复杂度分析 三.写在最后 一.快排时间复杂度分析 快速排序的时间复 ...

  2. 排序算法-快速排序的时间复杂度分析

    快速排序的思想是在数组[p,r]中选择一个分区点q,将数组一分为2,同时将小于分区点的数值的放到分区点左侧[p,q-1],大于分区点的数值的放到分区点右侧[q+1,r],重复这个过程. 快速排序也是用 ...

  3. 快速排序 C++代码实现及其算法思想及时间复杂度分析及优化 恋上数据结构笔记

    文章目录 复习梗概 算法思想 算法复杂度分析及稳定性 如何优化? 快速排序改进版代码C++ 快速排序个人青春版代码 完整代码 复习梗概 算法思想,别的排序名字直接就能让人联想到它的算法思想,唯独快速排 ...

  4. 关于快速排序和归并排序的时间复杂度分析

    目录 一.快排时间复杂度分析 二.归并排序时间复杂度分析 三.写在最后 一.快排时间复杂度分析 快速排序的时间复杂度在O(nlogn)~ O(n^2)之间,下面我分别分析这两种情况: (一)快速排序的 ...

  5. 归并排序执行次数_归并排序过程、时间复杂度分析及改进

    前言 上一篇文章,介绍过第一种基于分治策略的排序算法--快速排序.接下来我们来讨论另一种基于分治策略的排序算法,归并排序.归并排序也被认为是一种时间复杂度最优的算法,我们还是按照基本过程,代码,最坏时 ...

  6. 算法时间复杂度分析基础

    摘要       本文论述了在算法分析领域一个重要问题--时间复杂度分析的基础内容.本文将首先明确时间复杂度的意义,而后以形式化方式论述其在数学上的定义及相关推导.从而帮助大家从本质上认清这个概念. ...

  7. 希尔排序选择排序时间复杂度分析

    #include <stdio.h> #include <stdlib.h>void shellsort(char array[],int len); void selects ...

  8. 快速排序的时间复杂度与空间复杂度

    C/C++中快速排序的时间空间复杂度分析 1.什么是快速排序 我理解的是,快速排序用的是分治法,运用的递归的算法,先挑选一个基准值,小于基准值的数放在左边, 大于基准值的数放在基准值的右边,这样就泾渭 ...

  9. python数据结构和算法 时间复杂度分析 乱序单词检测 线性数据结构 栈stack 字符匹配 表达式求值 queue队列 链表 递归 动态规划 排序和搜索 树 图

    python数据结构和算法 参考 本文github 计算机科学是解决问题的研究.计算机科学使用抽象作为表示过程和数据的工具.抽象的数据类型允许程序员通过隐藏数据的细节来管理问题领域的复杂性.Pytho ...

最新文章

  1. 设置VSCode打开键盘快捷方式和键盘快捷方式配置JSON分别快捷键为:Ctrl+Alt+K和Shift+Alt+K
  2. 31 元素滚动scroll系列
  3. python使用imbalanced-learn的ClusterCentroids方法进行下采样处理数据不平衡问题
  4. tensorflow.transpose() 举例
  5. Golang 新手可能会踩的 50 个坑
  6. 云原生生态周报 Vol. 13 | Forrester 发布企业级容器平台报告
  7. 编译期间确定类型安全——泛型(Generics)
  8. vue 微信开发工具 Maximum call stack size exceeded
  9. 第一个JavaWeb项目——教室预约系统
  10. centos7 zabbix
  11. ECharts图表tooltip显示时超出canvas图层解决方法
  12. autojs-识别验证码-联众打码
  13. android的终端模拟器,安卓开发者必备的 5 款 App:终端模拟器、颜色萃取、移动 IDE 等...
  14. cmd批处理的/d/l/r/f
  15. Notepad++插件: HexEditor
  16. 智能秤方案设计——蓝牙体脂秤PCBA软硬件端功能说明
  17. PDF如何插入空白页?3 次点击在PDF中插入空白页!
  18. 以下为一个有理数类,一个有理数由分子和分母组成,目前定义了一个输出函数 十七、编写一个加法函数,可以接收另一个有理数,与自身的值相加 十八、编写一个乘法函数,同理接收另一个有理数,与自身的值相乘
  19. python编程从入门到实践练习8-6:城市名
  20. a标签做按钮效果,并实现单选效果

热门文章

  1. 计算某天是星期几的方法
  2. Disgruntled Judge UVA - 12169
  3. LeetCode--11_974_Subarray_Sums_Divisible_by_K
  4. git 命令使用技巧
  5. Burnside引理与Pólya定理
  6. 第五百八十天 how can I 坚持
  7. 深入了解回调函数Java
  8. Httpclient 实现带参文件上传
  9. 微软VS2008月底推出beta 2中文版 搭配.NET 3.5
  10. matlab中size、length、cell的用法