算法导论 第7章 课后习题

7.2-4 银行经常按照交易时间,来记录有关某一账户的交易情况,但是,很多人喜欢按照票据号来收到其银行对账单。因此,如何将按交易时间排序转换成按支票编号来排序,就成为一个对几乎排好序的输入进行排序的问题。证明在这个问题上,过程INSERT-SORT的性能往往优于过程QUIKSORT。

    问题解析:
    对于QUIKSORT来说,输入一个已排序的数组属于最坏的情况,则每次区间划分都是最大程度的不对称。其算法运行的递归时间为T(n) = T(n-1) + Θ(n), 算法时间复杂度为Θ(n^2); 而对INSERT-SORT来说,输入一个已排序的数组却属于最佳的情况,算法时间复杂度为O(n)。也就是说当输入一个几乎排好序的数组,快速排序趋于最坏的情况,而插入排序却趋于最佳的情况。为降低这种最坏情况出现的概率,采用快速排序随机化版本,期望的运行时间为O(nlgn)。
 
7.3-2 在过程RANDOMIZED-QUIKSORT的运行过程中,最坏情况下对随机排序产生器RANDOM调用了多少次?最佳情况下调用了多少次?以Θ记号形式个给出你的答案。
   问题分析:
        随机快速排序中,只要区间包含元素数目大于1,则需调用RANDOMIZED-PARTITION,选取主元(pivot)进行区间划分, 而主元的选取需调用Random。主元(pivot)一旦被选出来后,就不会在加入到后续的排序了。直白来说就是,有多少次主元(pivot)选取就有多少次随机数生成。另一方面从算法的递归二叉树树上来看,递归树二叉树的非叶子节点可表示一个主元(pivot);而叶子节点分为两种,一种节点是包含0个元素,另一种节点是包含1个元素,而且该元素不是主元(pivot)。非叶子节点和包含1个元素的叶子节点的数目之和就是输入序列的大小n。即递归树的非叶子节点的数目就是调用RANDOM的次数。调用RANDOM次数T(RANDOM)≤n,即T(RANDOM)=O(n)。
  又根据二叉树的性质,对于任意一棵二叉树,如果其叶结点数为a,而度数为2的结点(非叶子节点)总数为b,则a=b+1;对于快速排序的递归二叉树中,非叶子节点度数均为2。假设含有0个元素的叶子节点数目a0(≥0),含有1个元素的叶子节点数目为a1,所以 b =a-1 ≥ a1 - 1;又b+a1 = n,可得 b ≥ n/2 - 1,即T(RANDOM)=Ω(n)。   
  总的来说T(RANDOM)=Θ(n) 
思考题:
7-3 Howard、Fine等教授提出了下面的“漂亮的”排序算法。
STOOGE-SORT(A, i, j)
1        if A[i]>A[j]
2             then exchange A[i]<->A[j]
3         if i+1>=j
4             then return
5         k<-floor((j-i+1)/3)
6         STOOGE-SORT(A, i, j-k)
7         STOOGE-SORT(A, i+k, j)
8         STOOGE-SORT(A, i, j-k)
a) 证明:如果n=Length[A],那么STOOGE-SORT(A, 1, Length[A])能正确地对输入数组A[1..n]进行排序。
b) 给出一个表达STOOGE-SORT最坏情况的运行时间的递归式,以及关于最坏情况运行时间的一个紧确的渐进(Θ记号)界。
 
a).算法分析:
    循环不变式:调用STOOGE-SORT(A, i, j)后,数组A[i...j]已有序。
    初始化:当i+1  j,则A[i, j]包含1个或2个元素。通过1和2行的比较和交换,然后直接返回即可。
    保持:假设STOOGE-SORT(A, i, j)内的递归调用STOOGE-SORT(A, i, j-k)、STOOGE-SORT(A, i+k, j)、STOOGE-SORT(A, i, j-k)均满足循环不变式。   
    对区间A[i...j]进行划分
    A1=A[i...i+k-1], 
    A2 = A[i+k...j-k], 
    A3=A[j-k+1, j] ,
    且
    A[i...j-k] = [A1, A2] 
    A[i+k...j] = [A2, A3] 
    A[i...j] = [A1, A2, A3]
    因为k=floor( (j-i+1)/3 ),所以 3k ≤ j-i+1 ≤ 3k+2      ·····························(1)
    length[A1] = length[A3] = k
    length[A2] = j-i+1-2k
    由(1)式得,k ≤ length[A2] ≤ k+2                               ·····························(2)
    行6中调用STOOGE-SORT(A, i, j-k),执行结果是[A1, A2]升序排序,且A1 ≤ A2(表示A2所有元素均大于A1中的元素)。由(2)式中k ≤ length[A2],所以 [A1, A2] 的k个最大值一定在A2中。由此推知,[A1, A2, A3]的k个最大值一定在[A2, A3]中。 
    行7中调用STOOGE-SORT(A, i+k, j),执行结果是[A2, A3]升序排序 ,且A2 ≤ A3(表示A3所有元素均大于A2中的元素), A3包含 [A2, A3]的k个最大值。结合对行6的分析可得,A3包含[A1, A2, A3]的k个最大值,A1 ≤ A3 and A2 ≤ A3
    行8中调用STOOGE-SORT STOOGE-SORT(A, i, j-k)类似对行6的分析可得A1 ≤ A2。
    所以A1 ≤ A2 ≤ A3,且A1、A2、A3数组内部已有序。所以[A1, A2, A3](即A[i...j])已有序。
    终止:STOOGE-SORT(A, 1, n) 执行完后,可使数组 A 有序,算法是正确的。
b).复杂度分析:
    可推得STOOGE-SORT递归式为:
    T(n) = 3T(2*k) + O(1),k=floor( n/3 )
    忽略边界条件可得,
    T(n) = 3T(2n/3) + c
    由主定理可得
    a = 3 , b = 3/2, f(n) = c
    满足主定理第一种情况,所以T(n) = Θ(n^log(2/3, 3))    
7.4  7.1节中的QUICKSORT算法包含有两个对自身递归调用。在调用PARTITION后,左边的子数组和右边的子数组分别被递归排序。

QUICKSORT中第二次递归调用并不是必须的;可以用迭代控制结构来代替它。这种技术称作尾递归,对多数的编译程序都加以了采用。

考虑下面合格快速排序的版本,它模拟了尾递归:

QUICKSORT'(A, p, r)

1  while p < r

2        do  Partition and sort left subarray.

3             q ← PARTITION(A, p, r)

4             QUICKSORT'(A, p, q - 1)

5             p ← q + 1

a)论证QUICKSORT'(A, 1, length[A])能正确地对数组A进行排序。

b)请给出一种在含有n个元素的输入数组上QUICKSORT'的栈深度为Θ(n)的情况。

c)修改QUICKSORT'的代码,使其最坏情况下栈深度为Θ(lgn),保持算法的O(nlgn)期望运行时间不变。

a)    省略

   

 b)问题分析: 由于QUICKSORT'只是将一般快速排序算法中第二次递归调用替换为迭代控制结构,并未改变区间的划分方式。 迭代控制结构(while)不断对左侧区间进行递归调用,对右侧区间进行再次划分,循环往复,直到右侧区间为空或只剩包含1个元素。
    QUICKSORT'(A, p, r)算法迭代深度为D(A[p…r]). while循环一共运行了m次(即在m次循环后q≥r)在while循环迭代中 值一直不变, 设第i次循环开始时p=pi PARTITION选取的主元为q=qi。由5可得 p= qi-1  + 1。令q= p - 1。
 

    入栈操作是由于左侧区间的递归调用,为使栈深度趋于Θ(n),就需要左侧区间不断的递归调用Θ(n)次输入已升序排序数组A[p...r],每次区间划分都是主元(pivot)为A[r],左侧区间为A[p...r-1],右侧区间为空,所以在QUICKSORT'中的while只循环一次。由(2)式可推得

由递归式(3)可知

    

 
输入数组A为含有n个元素且已按升序排序,栈深度为

    
c)问题分析
    为保证最坏情况下栈深度为Θ(lgn),即在最坏情况下左侧区间连续的递归调用趋近于Θ(lgn)。如果每次划分都是最均衡的划分,左侧区间不可能大于n/2,递归的栈深度最多为Θ(lgn)。为保证每次划分都是均衡的划分,即每次PARTITION选取的主元(pivot)均为区间的中位数即可。
                     
原文地址:https://www.cnblogs.com/bigrabbit/p/2541356.html