zoukankan      html  css  js  c++  java
  • CLRS2e读书笔记—Chapter7

    快速排序相关C代码:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <time.h>
     4 void swap(int* A,int i,int j)
     5 {
     6     int temp=A[i];
     7     A[i]=A[j];
     8     A[j]=temp;
     9 }
    10 int Partition(int* A,int p,int r)
    11 {
    12     int x=A[r-1];
    13     int i=p-1,j=p;
    14     for(;j<r-1;++j) {
    15         if(A[j]<=x)
    16         {
    17             swap(A,++i,j);
    18         }
    19     }
    20     swap(A,++i,r-1);
    21     return i;
    22 }
    23 int Random_Partition(int* A,int p,int r)
    24 {
    25     srand((unsigned int)time(NULL));
    26     int i=rand()%r;
    27     swap(A,i,r-1);
    28     return Partition(A,p,r);
    29 } 30 int Original_Partition(int* A,int p,int r) 31 { 32 int x=A[p]; 33 int i=p-1,j=r; 34 while(1) 35 { 36 while(A[--j]>x); 37 while(A[++i]<x); 38 if(i<j) 39 swap(A,i,j); 40 else 41 return j; 42 } 43 } 44 void Quick_Sort(int* A,int p,int r) 45 { 46 if(p<r-1) 47 { 48 int q=Partition(A,p,r); 49 //对于Hoare版本,注意到主元并未独立出来 50 //int q=Original_Partition(A,p,r); 51 //Quick_Sort(A,p,q+1); 52 Quick_Sort(A,p,q); 53 Quick_Sort(A,q+1,r); 54 } 55 } 56 int main() 57 { 58 int A[30]; 59 int i=0; 60 printf("Original:\n"); 61 for (;i<30;i++) 62 { 63 A[i]=rand()%10; 64 printf("%d\t",A[i]); 65 } 66 Quick_Sort(A,0,30); 67 printf("\nRearrange:\n"); 68 for(i=0;i<30;++i) 69 { 70 printf("%d\t",A[i]); 71 } 72 printf("\n"); 73 }

    约定起始下标是0,终止的下标是末尾后一个(类似C++的迭代器)。这样就与书中1-n有差别(这里是0-n,n不可达)。上一章也是这么写的,这里做一下注释。

    本章大部分篇幅用于分析快排的性能问题,使用指示器随机变量的版本比原来的容易理解多了…【MIT的公开课用的仍然是first edition的,木有引入这个概念】。

    exercises:

    7.1-2 q=r;修改Partition使得q=$\lfloor (p+r)/2 \rfloor$的方法其实很多,因为这里没有限制不能显式检查这种情况…不过最好的方法还是使用Hoare原版的划分方法(见思考题7-1)。

    7.1-3 line4到line6是常数级消耗,即Θ(1),那么主要消耗在line3的循环上,为Θ(n-1)所以总的运行时间为Θ(n)。

    7.1-4 将line4的判断符号由≤改为≥即可。

    7.2-1 \[ \begin{split}T(n) &= T(n-1)+\Theta(n) \\ &= \Theta(n-1)^2+\Theta(n)\\ &\le c(n-1)^2+dn\\ &\le cn^2 = \Theta(n^2) \end{split} \]

    7.2-2,7.2-3 都是相同值和已经排好序的情况类似,都是最坏情况,前者要进行n次自交换,后者划分一定是不均匀的,两种情况最后返回的j值都在数组尾部。这样需要进行的划分次数必须是n,那么运行时间就是Θ(n2)。

    7.2-4 在几乎已经排好序的序列上进行排序,这时快排接近7.2-3中的最坏情况;而对于插入排序,其内循环中确定位置需要遍历元素的个数大幅度减少,使得整个算法的时间趋近于Θ(n),也就是liner time。

    7.2-5 递归树的表达式为T(n)=T(αn)+T((1-α)n]+Θ(n),由于alpha≤1/2,那么递归树最后到达叶节点的分支表达式必定是(1-α)h*n=1,解得h=-lgn/lg(1-α);

    而最先到达叶子结点的高度h必定满足αh*n=1,也可以解出题设给出的答案。

    7.2-6 可以通过作图简单得证,将长度为1的线段划为1-α和α两部分(α≤1/2),显然更均匀的划分比定位于α与1/2之间,或者与其以1/2为轴对称的区间内,所以总的长度为2×(0.5-α)即1-2α。

    7.3-1 分析最坏情况毫无意义,因为随机化的最坏情况与不随机的最坏情况是一致的。

    7.3-2 随机数发生器位于Random-Partition中,所以最坏情况下调用Θ(n)次,最佳情况Θ(lgn)次。

    7.4-1 代换法,注意

    \[ \max_{0 \le q \le n-1}(T(q)+T(n-q-1)) \ge \frac{(n-1)^2}{2} \]

    这里用了代数上的不等式知识。

    7.4-2 最佳情况即T(n)=2T(n/2)+Θ(n),但是严谨的证明方式是使用类似7.4-1中的递归式,然后使用不等式和代换法证明该下界。

    7.4-3 这里可以用对应的连续函数的导数来证明单调性,然后求出最大值所在的点。

    7.4-4 正文中已经证明了…

    7.4-5 如果需要在小数组上实现插入排序,只要修改

    Quick-Sort(A,p,r)

    if r-p+1≥k

      then ...

    使用Sort调用Quick-Sort,然后再调用Insertion-Sort即可。

    期望运行时间的证明比较麻烦,对于Insertion-Sort部分,由于未排序的片段长度小于k,所以内循环的数据移动长度必定<k,即整体的运行时间<Θ(nk)

    下面需要证明插排部分的期望运行时间≤Θ(nlg(n/k)),使用类似正文中统计总partition次数的方法,最后

    \[ E[x]=\Sigma_{i=1}^{n-k-1}\Sigma_{m=k}^{n-i}\frac{2}{m+1} < \Sigma_{i=1}^{n-k-1} (lgn-lgk) \le nlg(n/k) \]

    总结以上,得证(但是这里的证明似乎有些问题,主要考虑到n是1到∞,但是k肯定是小于n的,直接使用收敛和公式得出最后的结果恐怕不妥…)

    实际使用中,k值的确定方法很简单:只要在任意k长度的小数组上插排的速度正好等于或者快于快排,那么就取这个临界值作为k。

    7.4-6 这题的意思总结一下就是:如果实行“三位取中”的策略,如何能保证最后这个中位数左边正好有αn-1个数,右边正好有(1-α)n个数【或者正好相反】,那么

    \[ Pr=2 \frac{(\alpha n-1)(n-\alpha n)+\alpha n(n-\alpha n-1)}{\binom{3}{n}} \]

    经过化简计算,结果应该是$\frac{2\alpha n-2 {\alpha}^2 n-1}{n^2-3n+2}$

    以上结果建立在所有元素都不相等的情况下…

    Problems:

    7-1 这里就是Hoare最初提出的Quick-Sort的伪代码了,在很多值相等时,这种写法的效率更高(当然平均效率还是nlgn)。

      这里比较难以理解的地方是每轮划分后主元的位置并未独立出来,而是仍然被放入下一轮继续排列。所以Quick-Sort的代码有些许对应的微调。

      循环不变式见本题b-d,主要思想就是[p,j]的值≤[j+1,r]的值。

    7-2 不引入指示器随机变量的分析版本,计算比较复杂,不过该证明的可扩展性可能更好吧。俺数学不行,就略过这题了。

    7-3 一个蛋疼的排序法,T(n)=3T(2n/3)+Θ(1),由主方法可得复杂度为Θ(n2.7)左右【lg3/lg1.5】。

    7-4 将尾部递归改为迭代,以减少对堆栈的使用。

    Quick-Sort'(A,p,r)

    while p<r

      do $\vartriangleright$Partition and sort left subarray

      q←Partition(A,p,r)

      Quick-Sort'(A,p,q-1)

      p←q+1

    b> 仍然是最坏情况划分会导致堆栈深度为Θ(n).

    c>先排序长度较小的一边,这样递归树会以≤Θ(lgn)的深度到达叶子结点,即

    Quick-Sort''(A,p,r)

    while p<r

      do $\vartriangleright$Partition and sort left subarray

      q←Partition(A,p,r)

      if q-p≥r-q

        then Quick-Sort''(A,q+1,r)

            r←q-1

      else

            Quick-Sort''(A,p,q-1)

           p←q+1

    7-5 概率分析问题,参加7.4-6

    7-6 这里只要明确如何判定两个区间的关系就很容易写出算法。

    设定区间$[a_i,b_i]=i$和$[a_j,b_j]=j$,则

    if $a_j>b_i$,i<j

    if $a_i>b_j$,i>j

    其他情况下二者相等【换言之:只要两者有重合区间就认为两区间相等】

    以这个判定方法使用快速排序(原版)含有该区间的数组(结构数组)进行排序即可。

  • 相关阅读:
    远程连接桌面报:这可能是由于credssp加密oracle修正
    MVC断点续传
    [COCI2011-2012#5] POPLOCAVANJE 后缀自动机
    [SDOI2016]生成魔咒 后缀自动机
    [JSOI2009]密码 AC自动机
    CF17E Palisection manacher
    [JSOI2007]字符加密 后缀数组
    [POI2012]OKR-A Horrible Poem hash
    [APIO2014]回文串 manacher 后缀数组
    [SHOI2011]双倍回文 manacher
  • 原文地址:https://www.cnblogs.com/livewithnorest/p/2657796.html
Copyright © 2011-2022 走看看