Exercises
7.3-1 为什么我们分析随机化算法的期望运行时间,而不是最坏运行时间呢?
随机化算法并不会改善最坏情况的运行时间,但是会减少最坏情况发生的概率。
7.3-2 在RANDOMIZED-QUICKSORT的运行过程中,在最坏情况下,随机数生成器RANDOM被调用了多少次?在最好情况下呢?以
Θ符号的形式给出你的答案?
因为排列
n个数最好情况和最坏情况下都需要选取
n−1次主元,所以
最坏情况下:
Θ(n)
最好情况下:
Θ(n)
7.4-1 证明:在递归式
T(n)=max(T(q)+T(n−q−1))+Θ(n)
中,
T(n)=Ω(n2)
假设
T(n)≥cn2,则
T(n)≥max(cq2+c(n−q−1)2)+Θ(n)
q2+(n−1−q)2的最大值在两端点处取得,我们有
T(n)≥c(n−1)2+Θ(n)=cn2−c(2n−1)+Θ(n)
只要取足够小的正常数c,使得
c(2n−1)≤Θ(n),就有
T(n)≥cn2,即
T(n)=Ω(n2)。
7.4-2 证明:在最好情况下,快速排序的运行时间为
Ω(lgn)。
最好情况下,有递归式:
T(n)=2T(n/2)+Θ(n)
假设
T(n)≥cnlgn,则
T(n)≥2c2nlg2n+Θ(n)=2cnlgn−2cn+Θ(n)
只要取足够小的正常数c,使得
2cn<Θ(n),就有
T(n)≥cnlgn,即
T(n)=Ω(nlgn)
7.4-3 证明:在
q=0,1,⋯,n−1区间内,当
q=0或
q=n−1时,
q2+(n−q−1)2取得最大值。
令
f(q)=q2+(n−1−q)2,则有
f′(q)=4q−2(n−1),f′′(q)=4,
f(q)在
(0,2n−1)上单调递减,在
(0,2n−1)上单调增加,在
q=2n−1上取得极小值,在两端点处取得最大值
f(0)=f(n−1)=(n−1)2。
7.4-4 证明:RANDOMIZED-QUICKSORT的期望运行时间是
Ω(nlgn)。
7.4-5 当输入数据已经“几乎有序”时,插入排序速度很快。在实际应用中,我们可以利用这一特点来提高快速排序的速度。当对一个长度小于
k的子数组调用快速排序时,让它不做任何排序就返回。当上层的快速排序调用返回后,对整个数组运行插入排序来完成排序过程。试证明:这一排序算法的期望复杂度为
O(nk+nlog(n/k))。分别从理论和实践的角度说明我们应该如何选择
k。
算法代码如下:
QUICKSORTWITHINSERTIONSORT(A,p,r)
QUICKSORT(A,p,r)
INSERTION-SORT(A,p,r)//整体进行插入排序O(nk)
QUICKSORT(A,p,r)
if r-p+1>k //长度小于k的子数组直接返回
q=PARTITION(A,p,r)
QUICKSORT(A,p,q-1)
QUICKSORT(A,q+1,r)
总体排序时间时快速排序和插入排序总和。快速排序递归树递归到
T(k)为止,递归树高度此时为
O(logn−logk),每层划分花费
O(n),一共
O(nlog(n/k))。插入排序时,每个元素的移动不会超过
k−1次,时间复杂度为
O(nk)。所以总的时间复杂度为
O(nk+nlog(n/k))。
理论上不要超过快速排序的平均时间复杂度
O(nlogn)。实践中,
k在
[1,logn]之间取值。
Problems
7.4 (快速排序的栈深度)7.1节中的QUICKSORT算法包含了两个对其自身的递归调用。在调用PARTITION后,QUICKSORT分别递归调用了左边的子数组和右边的子数组。QUICKSORT中第二个递归调用不是必须的。我们可以用一个循环空值结构来代替它。这一技术成为尾递归,好的编译器都提供这一功能。考虑下面这个版本的快速排序,它模拟了尾递归的情况:
TAIL-RECURSIVE-QUICKSORT(A,p,r)
while p < r
//Partition and sort left subarray.
q-PARTITION(A,p,r)
TAIL-RECURSIVE-QUICKSORT(A,p,q-1)
p=q+1
a.证明:TAIL-RECURSIVE-QUICKSORT(A,1,A.length)能正确地对数组A进行排序。编译器通常使用栈来存储递归执行过程中的信息,包括每一次递归调用的参数等。最新调用的信息存在栈的顶部,而第一次调用的信息存在栈的底部。当一个过程被调用时,其相关信息被压入栈中;当它结束时,其信息被弹出。因为我们假设数组参数是用指针来指示的,所以每次过程调用只需要
O(1)的栈空间。栈深度是在一次计算中会用到的栈空间的最大值。
b.请描述一种场景,使得针对一个包含
n个元素数组的TAIL-RECURSIVE-QUICKSORT的栈深度是
Θ(n)。
输入序列是
A=<1,2,3,4,⋯,n>时,栈深度是
Θ(n)
c.修改TAIL-RECURSIVE-QUICKSORT的代码,使其最坏情况下栈深度是
Θ(lgn),并且能够保持
O(nlgn)的期望时间复杂度。