算法导论 — 7.2 快速排序的性能

本文深入探讨了快速排序算法的运行时间,包括最坏、最好及平均情况下的时间复杂度。通过分析不同输入条件下PARTITION函数的表现,揭示了快速排序性能的决定因素。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

笔记

本节只给出快速排序运行时间的简单分析。快速排序的运行时间取决于PARTITION划分是否平衡,而划分的平衡与否又取决于用于划分的主元的选择。
  1. 最坏情况时间复杂度
  对于一个含nnn个元素的数组,PARTITION划分的最坏情况为:划分产生的两个子数组的大小分别为n−1n-1n1000时。假设快速排序每一次递归调用PARTITION都产生了最坏情况划分,那么算法的运行时间T(n)T(n)T(n)满足以下递归式。
  T(n)=T(n−1)+T(0)+Θ(n)=T(n−1)+Θ(n)T(n) = T(n-1) + T(0) + Θ(n) = T(n-1) + Θ(n)T(n)=T(n1)+T(0)+Θ(n)=T(n1)+Θ(n)
  求解这个递归式得到T(n)=Θ(n2)T(n) = Θ(n^2)T(n)=Θ(n2)。也就是说,如果快速排序的每一次递归调用都产生了最坏情况划分,那么算法的时间复杂度为Θ(n2)Θ(n^2)Θ(n2)
  快速排序最坏情况的一个例子是,输入数组已经完全有序,此时快速排序的时间复杂度为Θ(n2)Θ(n^2)Θ(n2)。而同样的情况下,如果采用插入排序,时间复杂度为Θ(n)Θ(n)Θ(n)。因此,在数组已经有序的时候,插入排序要优于快速排序。
  2. 最好情况时间复杂度
  快速排序的最好情况出现在:每一次PARTITION都得到一个完全平衡的划分,即对于一个含nnn个元素的数组,PARTITION产生的两个子数组的大小分别为⌊(n−1)/2⌋⌊(n-1)/2⌋(n1)/2⌈(n−1)/2⌉⌈(n-1)/2⌉(n1)/2。假设快速排序每一次递归调用都产生了最好情况划分,那么算法的运行时间T(n)T(n)T(n)满足以下递归式。
  T(n)=2T(n/2)+Θ(n)T(n) = 2T(n/2) +Θ(n)T(n)=2T(n/2)+Θ(n)
  求解这个递归式得到T(n)=Θ(nlgn)T(n) = Θ(nlgn)T(n)=Θ(nlgn),这就是快速排序最好情况的时间复杂度。
  3. 平均情况的时间复杂度
  本节给出了结论:快速排序的平均情况时间复杂度为Θ(nlgn)Θ(n{\rm lg}n)Θ(nlgn)。本节对这一结论只做了简要的说明,在7.4节会给出严格的证明,我们先记下这个结论。

练习

7.2-1 利用代入法证明:正如7.2节开头提到的那样,递归式T(n)=T(n−1)+Θ(n)T(n) = T(n-1) + Θ(n)T(n)=T(n1)+Θ(n)的解为T(n)=Θ(n2)T(n) = Θ(n^2)T(n)=Θ(n2)
  
  
  
7.2-2 当数组AAA的所有元素都具有相同的值时,QUICKSORT的时间复杂度是什么?
  
  利用练习7.1-2的结论,当所有元素都相同时,每次调用PARTITION都产生一个最坏情况划分,因此这种情况下,QUICKSORT的时间复杂度为Θ(n2)Θ(n^2)Θ(n2)
  
  
7.2-3 证明:当数组AAA包含的元素各不相同,并且是按降序排列的时候,QUICKSORT的时间复杂度为Θ(n2)Θ(n^2)Θ(n2)
  
  考虑包含nnn个元素的数组AAA,其中元素各不相同,并且按降序排列,即a1>a2>…>ana_1 > a_2 > … > a_na1>a2>>an。我们来分析每次调用PARTITION的情况。
  在这里插入图片描述
  第111次划分,调用PARTITION(A,1,n){\rm PARTITION}(A, 1, n)PARTITION(A,1,n),由于被选为划分主元的ana_nan是最小的,所以最终a1a_1a1ana_nan交换,产生最坏情况划分。
  第222次划分,调用PARTITION(A,2,n){\rm PARTITION}(A, 2, n)PARTITION(A,2,n),由于被选为划分主元的a1a_1a1是最大的,所以这次划分没有任何改变,也是一个最坏情况划分。
  接下来的第333次划分,调用PARTITION(A,2,n−1){\rm PARTITION}(A, 2, n-1)PARTITION(A,2,n1),此时数组A[2..n−1]A[2..n-1]A[2..n1]又是一个降序排列的数组,因此也产生一个最坏情况划分。
  如此循环往复下去,每次调用PARTITION{\rm PARTITION}PARTITION都产生一个最坏情况划分。所以数组按降序排列的情况下,QUICKSORT的时间复杂度为Θ(n2)Θ(n^2)Θ(n2)
  
  
7.2-4 银行一般会按照交易时间来记录某一账户的交易情况。但是,很多人却喜欢收到的银行对账单是按照支票号码的顺序来排列的。这是因为,人们通常都是按照支票号码的顺序来开出支票的,而商人也通常都是根据支票编号的顺序兑付支票。这一问题是将按交易时间排序的序列转换成按支票号排序的序列,它实质上是一个对几乎有序的输入序列进行排序的问题。请证明:在这个问题上,INSERTION-SORT的性能往往要优于QUICKSORT。
  
  根据前面的分析,对于一个几乎有序的数组,插入排序接近它的最好情况,时间复杂度接近Θ(n)Θ(n)Θ(n)。而快速排序接近它的最坏情况,时间复杂度接近Θ(n2)Θ(n^2)Θ(n2)。所以插入排序的性能要优于快速排序。
  
  
7.2-5 假设快速排序的每一层所做的划分的比例都是1−α:α1-α:α1αα,其中0&lt;α≤1/20 &lt; α ≤ 1/20<α1/2且是一个常数。试证明:在相应的递归树中,叶结点的最小深度大约是lgn/lgα{\rm lg}n/{\rm lg}αlgn/lgα,最大深度大约是lgn/lg(1−α){\rm lg}n/{\rm lg}(1-α)lgn/lg(1α)(无需考虑整数舍入问题)。
  
  在递归树中,最小深度的叶结点出现在每次划分比例为ααα的分枝,即子问题规模较小的那个分枝,它的深度为logαn=lgn/lgα{\rm log}_αn = {\rm lg}n/{\rm lg}αlogαn=lgn/lgα
  在递归树中,最大深度的叶结点出现在每次划分比例为1−α1-α1α的分枝,即子问题规模较大的那个分枝,它的深度为log1−αn=lgn/lg(1−α){\rm log}_{1-α}n = {\rm lg}n/{\rm lg}(1-α)log1αn=lgn/lg(1α)
  
  
7.2-6 试证明:在一个随机输入数组上,对于任何常数0&lt;α≤1/20 &lt; α ≤ 1/20<α1/2,PARTITION产生比1−α:α1-α:α1αα更平衡的划分的概率约为1−2α1-2α12α
  
  对数组的划分取决于被选择的划分主元。假如数组大小为nnn。如果划分主元是数组中最大的αnαnαn个元素中的一个,那么对数组的划分一定不会比1−α:α1-α:α1αα更平衡。如果划分主元是数组中最小的αnαnαn个元素中的一个,那么对数组的划分也一定不会比1−α:α1-α:α1αα更平衡。对于数组中间的n−2αnn-2αnn2αn个元素,如果划分主元是其中一个,那么产生的划分才会比1−α:α1-α:α1αα更平衡。
  对于一个随机输入数组,数组中的每一个元素都有相同的概率被选为划分主元。根据前面的分析,产生比1−α:α1-α:α1αα更好的划分的概率,也等于划分主元为中间的n−2αnn-2αnn2αn个元素之一的概率,这个概率为(n−2αn)/n=1−2α(n-2αn)/n = 1-2α(n2αn)/n=12α

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值