笔记
本节只给出快速排序运行时间的简单分析。快速排序的运行时间取决于PARTITION划分是否平衡,而划分的平衡与否又取决于用于划分的主元的选择。
1. 最坏情况时间复杂度
对于一个含
n
n
n个元素的数组,PARTITION划分的最坏情况为:划分产生的两个子数组的大小分别为
n
−
1
n-1
n−1和
0
0
0时。假设快速排序每一次递归调用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(n−1)+T(0)+Θ(n)=T(n−1)+Θ(n)
求解这个递归式得到
T
(
n
)
=
Θ
(
n
2
)
T(n) = Θ(n^2)
T(n)=Θ(n2)。也就是说,如果快速排序的每一次递归调用都产生了最坏情况划分,那么算法的时间复杂度为
Θ
(
n
2
)
Θ(n^2)
Θ(n2)。
快速排序最坏情况的一个例子是,输入数组已经完全有序,此时快速排序的时间复杂度为
Θ
(
n
2
)
Θ(n^2)
Θ(n2)。而同样的情况下,如果采用插入排序,时间复杂度为
Θ
(
n
)
Θ(n)
Θ(n)。因此,在数组已经有序的时候,插入排序要优于快速排序。
2. 最好情况时间复杂度
快速排序的最好情况出现在:每一次PARTITION都得到一个完全平衡的划分,即对于一个含
n
n
n个元素的数组,PARTITION产生的两个子数组的大小分别为
⌊
(
n
−
1
)
/
2
⌋
⌊(n-1)/2⌋
⌊(n−1)/2⌋和
⌈
(
n
−
1
)
/
2
⌉
⌈(n-1)/2⌉
⌈(n−1)/2⌉。假设快速排序每一次递归调用都产生了最好情况划分,那么算法的运行时间
T
(
n
)
T(n)
T(n)满足以下递归式。
T
(
n
)
=
2
T
(
n
/
2
)
+
Θ
(
n
)
T(n) = 2T(n/2) +Θ(n)
T(n)=2T(n/2)+Θ(n)
求解这个递归式得到
T
(
n
)
=
Θ
(
n
l
g
n
)
T(n) = Θ(nlgn)
T(n)=Θ(nlgn),这就是快速排序最好情况的时间复杂度。
3. 平均情况的时间复杂度
本节给出了结论:快速排序的平均情况时间复杂度为
Θ
(
n
l
g
n
)
Θ(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(n−1)+Θ(n)的解为
T
(
n
)
=
Θ
(
n
2
)
T(n) = Θ(n^2)
T(n)=Θ(n2)。
略
7.2-2 当数组
A
A
A的所有元素都具有相同的值时,QUICKSORT的时间复杂度是什么?
解
利用练习7.1-2的结论,当所有元素都相同时,每次调用PARTITION都产生一个最坏情况划分,因此这种情况下,QUICKSORT的时间复杂度为
Θ
(
n
2
)
Θ(n^2)
Θ(n2)。
7.2-3 证明:当数组
A
A
A包含的元素各不相同,并且是按降序排列的时候,QUICKSORT的时间复杂度为
Θ
(
n
2
)
Θ(n^2)
Θ(n2)。
解
考虑包含
n
n
n个元素的数组
A
A
A,其中元素各不相同,并且按降序排列,即
a
1
>
a
2
>
…
>
a
n
a_1 > a_2 > … > a_n
a1>a2>…>an。我们来分析每次调用PARTITION的情况。
第
1
1
1次划分,调用
P
A
R
T
I
T
I
O
N
(
A
,
1
,
n
)
{\rm PARTITION}(A, 1, n)
PARTITION(A,1,n),由于被选为划分主元的
a
n
a_n
an是最小的,所以最终
a
1
a_1
a1与
a
n
a_n
an交换,产生最坏情况划分。
第
2
2
2次划分,调用
P
A
R
T
I
T
I
O
N
(
A
,
2
,
n
)
{\rm PARTITION}(A, 2, n)
PARTITION(A,2,n),由于被选为划分主元的
a
1
a_1
a1是最大的,所以这次划分没有任何改变,也是一个最坏情况划分。
接下来的第
3
3
3次划分,调用
P
A
R
T
I
T
I
O
N
(
A
,
2
,
n
−
1
)
{\rm PARTITION}(A, 2, n-1)
PARTITION(A,2,n−1),此时数组
A
[
2..
n
−
1
]
A[2..n-1]
A[2..n−1]又是一个降序排列的数组,因此也产生一个最坏情况划分。
如此循环往复下去,每次调用
P
A
R
T
I
T
I
O
N
{\rm PARTITION}
PARTITION都产生一个最坏情况划分。所以数组按降序排列的情况下,QUICKSORT的时间复杂度为
Θ
(
n
2
)
Θ(n^2)
Θ(n2)。
7.2-4 银行一般会按照交易时间来记录某一账户的交易情况。但是,很多人却喜欢收到的银行对账单是按照支票号码的顺序来排列的。这是因为,人们通常都是按照支票号码的顺序来开出支票的,而商人也通常都是根据支票编号的顺序兑付支票。这一问题是将按交易时间排序的序列转换成按支票号排序的序列,它实质上是一个对几乎有序的输入序列进行排序的问题。请证明:在这个问题上,INSERTION-SORT的性能往往要优于QUICKSORT。
解
根据前面的分析,对于一个几乎有序的数组,插入排序接近它的最好情况,时间复杂度接近
Θ
(
n
)
Θ(n)
Θ(n)。而快速排序接近它的最坏情况,时间复杂度接近
Θ
(
n
2
)
Θ(n^2)
Θ(n2)。所以插入排序的性能要优于快速排序。
7.2-5 假设快速排序的每一层所做的划分的比例都是
1
−
α
:
α
1-α:α
1−α:α,其中
0
<
α
≤
1
/
2
0 < α ≤ 1/2
0<α≤1/2且是一个常数。试证明:在相应的递归树中,叶结点的最小深度大约是
l
g
n
/
l
g
α
{\rm lg}n/{\rm lg}α
lgn/lgα,最大深度大约是
l
g
n
/
l
g
(
1
−
α
)
{\rm lg}n/{\rm lg}(1-α)
lgn/lg(1−α)(无需考虑整数舍入问题)。
解
在递归树中,最小深度的叶结点出现在每次划分比例为
α
α
α的分枝,即子问题规模较小的那个分枝,它的深度为
l
o
g
α
n
=
l
g
n
/
l
g
α
{\rm log}_αn = {\rm lg}n/{\rm lg}α
logαn=lgn/lgα。
在递归树中,最大深度的叶结点出现在每次划分比例为
1
−
α
1-α
1−α的分枝,即子问题规模较大的那个分枝,它的深度为
l
o
g
1
−
α
n
=
l
g
n
/
l
g
(
1
−
α
)
{\rm log}_{1-α}n = {\rm lg}n/{\rm lg}(1-α)
log1−αn=lgn/lg(1−α)。
7.2-6 试证明:在一个随机输入数组上,对于任何常数
0
<
α
≤
1
/
2
0 < α ≤ 1/2
0<α≤1/2,PARTITION产生比
1
−
α
:
α
1-α:α
1−α:α更平衡的划分的概率约为
1
−
2
α
1-2α
1−2α。
解
对数组的划分取决于被选择的划分主元。假如数组大小为
n
n
n。如果划分主元是数组中最大的
α
n
αn
αn个元素中的一个,那么对数组的划分一定不会比
1
−
α
:
α
1-α:α
1−α:α更平衡。如果划分主元是数组中最小的
α
n
αn
αn个元素中的一个,那么对数组的划分也一定不会比
1
−
α
:
α
1-α:α
1−α:α更平衡。对于数组中间的
n
−
2
α
n
n-2αn
n−2αn个元素,如果划分主元是其中一个,那么产生的划分才会比
1
−
α
:
α
1-α:α
1−α:α更平衡。
对于一个随机输入数组,数组中的每一个元素都有相同的概率被选为划分主元。根据前面的分析,产生比
1
−
α
:
α
1-α:α
1−α:α更好的划分的概率,也等于划分主元为中间的
n
−
2
α
n
n-2αn
n−2αn个元素之一的概率,这个概率为
(
n
−
2
α
n
)
/
n
=
1
−
2
α
(n-2αn)/n = 1-2α
(n−2αn)/n=1−2α。