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

    凛冬将至。——《冰与火之歌》

    ————————————————————————————

    插入排序、合并排序:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <malloc.h>
     4 #include <string.h>
     5 void insertion_sort(int* array,int p,int q)
     6 {
     7     int j=p+1,i;
     8     for(;j<q;++j){
     9         int key=array[j];
    10         for(i=j-1;i>=0;--i){
    11             if(array[i]>key)
    12                 array[i+1]=array[i];
    13             else{
    14                 array[i+1]=key;
    15                 break;
    16             }
    17         }
    18     }
    19 }
    20 void merge(int* array,int p,int q,int r)
    21 {
    22     int n1=q-p;
    23     int n2=r-q;
    24     int *L=(int*)malloc(sizeof(int)*n1);
    25     int *R=(int*)malloc(sizeof(int)*n2);
    26     if(!L || !R)exit(EXIT_FAILURE);
    27     memcpy(L,array+p,sizeof(int)*n1);
    28     memcpy(R,array+q,sizeof(int)*n2);
    29 
    30     int i=0,j=0,k=p;
    31     while(i<n1 && j<n2)
    32     {
    33         if(L[i]<=R[j]) array[k++]=L[i++];
    34         else array[k++]=R[j++];
    35     }
    36     while(i<n1)array[k++]=L[i++];
    37     while(j<n2)array[k++]=R[j++];
    38     free(L);
    39     free(R);
    40 }
    41 void merge_sort(int* array,int p,int r)
    42 {
    43     if(p<r-1){
    44         int q=(r+p)/2;
    45         merge_sort(array,p,q);
    46         merge_sort(array,q,r);
    47         merge(array,p,q,r);
    48     }
    49 }
    50 int main()
    51 {
    52     int A[30];
    53     int i=0;
    54     printf("Original:\n");
    55     for (;i<30;i++)
    56     {
    57         A[i]=rand();
    58         printf("%d\t",A[i]);
    59     }
    60     //insertion_sort(A,0,30);
    61     //C中下标与范围并不一致,所以这里约定
    62     //第二个参数是不可达的
    63     merge_sort(A,0,30);
    64     printf("\nRearrange:\n");
    65     for(i=0;i<30;++i)
    66     {
    67         printf("%d\t",A[i]);
    68     }
    69     printf("\n");
    70 }

    exercises:

    2.1-4 

    Binary_Add(A,B,C)

    flag<-0

    for i<-1 to n

      do C[i]<-A[i]+B[i]+flag

           flag<-C[i]/2

           C[i]<-C[i] mod 2

    if flag=1 then

      C[n+1]<-1

    2.2-2 选择排序

    Selection-Sort(A)

    for i<-1 to n-1

      do minus<-A[i] and key <- i

         for j<- i+1 to n

          do if A[j] < minus then

            minus<-A[j]

            key <- j

        A[i]<->A[j]

    算法已经选出前n-1个最小的元素,那么第n个必然是最大的元素。最佳情况,已经排序好,显然是线性的$\Theta(n)$,最坏情况正好相反,为$\Theta(n^2)=\Theta(n-1+n-2+...+1)$

    2.3-4 将插入排序改成递归结构

    Insertion-Sort'(A,n)

    if n>1

        then Insertion-Sort'(A,n-1)

    Insertion(A,n-1,A[n])

    Insertion(B,size,elem)

    i <- size

    while i>0 and B[i]>elem

      do B[i+1] <- B[i]

        i <- i-1

    B[i+1] <- elem

    显然递归表达式为:

    \[
    T(n)=\begin{cases}
    \Theta(1)\quad& \text{if $n=1$}\\
    T(n-1)+\Theta(n)\quad& \text{if $n>1$}
    \end{cases}
    \]

    可知最坏情况渐近分析时间为 $\Theta(n^2)$

    2.3-5 二分查找

    递归版本:

    Binary-Search(A,p,r,a)

    if r>p then

      q<-(p+r)/2

      if A[q]>a

        then Binary-Search(A,p,q-1,a)

       else if A[q]<a

        then Binary-Search(A,q+1,r,a)

       else return q

    else if A[p]=a then return p

    else return nill

    迭代版本:

    Binary-Search'(A,p,r,a)

    while r $\ge$ p

      do if r=p

        then if A[p]=a 

           then return p

          else return nill

        else 

        do q<-(p+r)/2

          if A[q]>a

            then r <- q-1

            else if A[q]<a

              then p<-q+1

            else return q

    递归式很明显是$T(n)=2T(n/2)+\Theta(1)$,因此worst run-time 是$\Theta(lgn)$

    2.3-6 这里的意思就是将Insertion-Sort的第5-7行的线性查找插入位置的代码修改成通过二分查找来找位置。由2.3-5可知二分查找的时间复杂度为\Theta(lgn),那么修改后的Insertion-Sort的复杂度应该是lg1+lg2+lg3+...lg(n-1)=lg$\prod_{i=1}^{n-1}i$=lg((n-1)!)=$\Theta(nlgn)$

    但是很遗憾,这只是查找的复杂度,采用数组这种数据结构,在找到位置后必须将该位置右端的元素后移一位,这个时间计算在内,最后的复杂度仍然是$\Theta(n^2)$;如果采用链表的数据结构,就不需移动数据,而只用常数时间改变指针指向,这样复杂度就降低到$\Theta(nlgn)$.

    2.3-7 由于二分查找的时间为\Theta(lgn),因此这里只需对每个A[i]二分查找S-A[i]即可(需要先排序)。

    Problems:

    2-1 在合并排序中嵌入插入排序,改善小数组的排序性能。

    a>n/k*$\Theta(k^2)=\Theta(nk)$

    b>需要合并n/k个长度为k的子序列,先合并成2n/k个长度为2k的子序列,然后以此类推直到合成一个完整的序列,每层树的代价是n/k*$\Theta(2k)=\Theta(n)$

    树的深度h满足:$k·2^h=n$,解得h=lg(n/k),因此总的代价为$\Theta(nlg(n/k))$

    c>即计算 $\Theta(nk+nlg(n/k))$=$\Theta(nlgn)$成立的k的表达式,由于$\Theta$是弱表达式符号,显然只需k=$\Theta(lgn)$就能使等式左边舍去加号右边的项,进而使二者相等。至于为什么它是最大渐进表达式,因为显然比这个表达式更大就不能使等式成立,但是如果令k=1,显然等式也是成立的。

    d>在实际中,k值取恰好能够使快速排序代价小于合并排序的输入规模。

    2-2 冒泡排序

    Bubble-Sort

    for i<- 1 to length(A)

      do for j<- length(A) down to i+1

        do if A[j] < A[j-1]

          then A[j] <->A[j-1]

    显然冒泡排序的性能和插入排序差不多…这题主要考察循环不变式的证明。

    循环不变式的证明通常需要需要三步:初始化(即第一次迭代之前),保持(在迭代的过程中)和终止(迭代结束后),在这个过程中,必须证明循环不变式保持正确。循环不变式的关键在于观察循环过程中对不变量的累积。

    2-3 霍纳规则

    a>循环终止条件为i>=0,循环体只是不停的迭代,复杂度为$\Theta(1)$,所以渐进运行的时间是$\Theta(n)$

    b>朴素多项式求值,也就是常规的计算方式,将P(x)按常规方法展开:P(x)=$a_0+a_1x+a_2x^2+\cdots+a_nx^n,代码一目了然

    Naive-PE(a,x)

    y<-0

    z<-1

    for i<-0 to n

      do y<- y+$a_i$ * z

        z<-z*x

    显然这里的算法复杂度仍然是$\Theta(n)$,但是常数因子比逆序递归求解要大。

    2-4 逆序对

    b>显然完全逆序排列的数组拥有最多的逆序对,个数为(n-1)+(n-2)+...+1=n(n-1)/2对

    c>插入排序在数组几乎已经排好序的情况下性能最好,所以逆序对越多显然运行时间越长

    d>求逆序对的数目,按着提示修改合并排序,在合并时如果L[i]>R[j],就对逆序对个数进行统计。

    分析:对于每一个L[i]>R[j],L[i],L[i+1]...L[n1]与R[j],R[j+1]...R[n2]都组成逆序对,但是如果两边都统计的话,最后的结果会有重复叠加。

    注意到L中元素任意调换顺序不影响L中元素将R[j]构成逆序对的数目,而总的逆序对数目=L中逆序对的数目+R中的逆序对数目+L对R的逆序对数目。

    而L中逆序对的数目=L中左半边逆序对的数目+L中右半边逆序对的数目+二者相对的数目。这也是一个递归的公式,当递归达到边界时,L与R的元素都只有一个,不存在逆序对,所以所有逆序对的数目=L对R逆序对数目的和,因此对每个L[i]>R[j]有n1-i+1个逆序对,当R[j]的位置确定后,再累加R[j+1]对L的逆序对数目。

  • 相关阅读:
    家庭记账本(三)记录页面的完善
    家庭记账本(二)记录界面显示
    家庭记账本(一)页面绘制
    MyBaits增删改查
    2021.01.20
    Maven_依赖与生命周期
    1592. Rearrange Spaces Between Words
    1588. Sum of All Odd Length Subarrays
    933. Number of Recent Calls
    765. Couples Holding Hands
  • 原文地址:https://www.cnblogs.com/livewithnorest/p/2638385.html
Copyright © 2011-2022 走看看