《算法导论》[第2章] 算法入门-[2.3] 算法设计

|概念回顾|

分治策略:将原问题划分成n个规模较小而结构与原问题相似的子问题;递归地解决这些小问题,然后再合并其结果,就得到原问题的解。

分治模式在每一层递归上都有三个步骤:

  • 分解(Divde):将原问题分解成一系列子问题;
  • 解决(Conquer):递归地解答各子问题。若子问题足够小,则直接求解;
  • 合并(Combine):将子问题的结果合并成原问题的解。

:⌈x⌉ 表示大于或等于x的最小整数;⌊x⌋表示小于或等于x的最大整数。

|习题解答|

2.3-1 以图2-4为模型,说明合并排序在输入数组A={3,41,52,26,38,57,9,49}上的执行过程。

        3 9 26 38 41 49 52 57      
      3 26 41 52 & 9 38 49 57      
  3 41 & 26 52       38 57 & 9 49  
3 & 41   52 & 26   38 & 57   9 & 49

注:&为合并符号。

 

2.3-2 改写MERGE过程,使之不使用哨兵元素,而是在一旦数组元素L或R中的所有元素都被复制回数组A后,就立即停止,再将令一个数组中余下的元素复制回数组A中。

1、注释掉第8,9句。

2、在12和13句之间添加如下伪代码:

 do if i>n1
    then while j<=n2
        do A[k++]←R[j++]
           break do if j>n2
    then while i<=n1
        do A[k++]←L[i++]
           break

2.2-3 利用数学归纳法证明:当n是2的整数次幂时,递归式

证明:

1、当n=2时,T(n)=2lg(2)=2成立。

2、假设当n=2^k(k>1)时,等式成立,即T(2^k)=(2^k)lg(2^k)=k(2^k);又因为已知:n=2^k(k>1)时,有T(2^k)=2T((2^k)/2)+2^k,所以:

T(2^(k+1))= 2T((2^(k+1))/2)+2^(k+1)= 2T(2^k)+2^(k+1)= 2(k*(2^k))+2^(k+1)= (k+1)*2^(k+1)

3、所以,当n=2^(k+1) (k>1)时,等式也成立。即递归式的解确实为T(n)=nlg(n)。

 

2.3-4 插入排序可以如下改写成一个递归过程:为排序A[1..n],先递归地排序A[1..n-1],然后再接着将A[n]插入到已排序的数组A[1..n-1]中去。对于插入排序的这一递归版本,为它的运行时间写一个递归式。

 

2.3-5 回顾一下练习2.1-3中提出的查找问题,注意如果序列A是已排序的,就可以将该序列的中点与v进行比较。根据比较的结果,原序列中有一半就可以不用再做进一步的考虑了。二分查找(binary search)就是一个不断重复这一查找过程的算法,它每次都将序列余下部分分成两半,并只对其中的一半做进一步的查找。写出二分查找算法的伪代码,可以是迭代的,也可以是递归的。说明二分查找算法的最坏情况运行时间为什么是Θ(lgn)。

二分查找的伪代码如下(递归版本):

BINARY-SEARCH(A,low,high,value)
       if high<low
          then return –1
       mid←⌊low+(high-low)/2⌋
       if A[mid]>value
          then return BINARY-SEARCH(A,low,mid-1,value)
          else if A[mid]<value
                 then return BINARY-SEARCH(A,mid+1,high,value)
          else return mid

二分查找最坏的情况是直到最后次二分也未找到相应的值,假设总量n等于2的k次方,即n=2^k,每二分一次k减1,当k=0时,之前一次为最后一次二分,即共执行了k次。而k=lg(n),所以,最坏情况的运行时间为Θ(lgn)。

 

2.3-6 观察一下2.1节中给出的INSERTION-SORT过程,在第5~7行的while循环中,采用了一种线性查找策略,在已排序的子数组A[1..j-1]中(反向)扫描。是否可以改用二分查找策略(见练习2.3-5),来将插入排序的总体最坏情况运行时间改善至Θ(nlgn)?

答:不可以,运行时间仍为Θ(n²),因为虽然查找过程运行时间从原来的Θ(n)[顺序查找]减少到了Θ(lgn)[二分查找],但找到恰当的位置之后,其后面的一系列数组元素就都要向后移动一位,平均运行时间也达到了Θ(n),所以在这里完成插入过程也需要花费Θ(n),那么,当排序完n个数时,同样,我们看到也需要花费Θ(n²)。

具体查看如下代码(C语言实现):

int BinarySort(int A[],int low,int high,int value)
{
    if(high<low)
        return -1;
    int mid=low+(high-low)/2;
    if(A[mid]>value) {
        if(value>A[mid-1])
            return mid;
        else
        return BinarySort(A,low,mid-1,value);
    }
    else if(A[mid]<value) {
        if(value<=A[mid+1])
            return mid+1;
        else
        return BinarySort(A,mid+1,high,value);
    }
    else return mid;
}

void InsertionBinarySort(int A[],int n)
{
    int i,j,key,label;
    for(j=2;j<=n;++j) {
        key=A[j];
        label=BinarySort(A,1,j-1,key);
        for(i=j-1;i>=label;--i)
            A[i+1]=A[i];
        A[label]=key;
    }
}

*2.3-7 请给出一个运行时间为Θ(nlgn)的算法,使之能在给定一个由n个整数构成的集合S和另一个整数x时,判断出S中是否存在有两个其和等于x的元素。

我的方案如下:先用快速排序QUICKSORT对合集S进行从小到大排序,然后for循环遍历S,在每一次循环中先使y=x-S[i],并将S[i]暂时赋值为y+1,防止寻找到同一个数,再用二分算法从S中查找y,若找到,则说明有在S中存在两个其和等于x的元素(一个是y,一个是刚刚的S[i]),输出Yes并结束for循环;反之,则先复原S[i]的值,避免影响到下一趟二分查找,直到遍历完所有元素,若仍未找到相应的y,则输出No。

运行时间分析:第一部分,快速排序期望的运行时间为Θ(nlgn);第二部分,二分查找运行时间为Θ(lgn)(具体可参考练习2.3-5),加上一个用来遍历S的for循环之后(运行时间为Θ(n)),总共运行时间也为Θ(nlgn)。综上,最后整个算法的运行时间即为Θ(nlgn),基本满足题意要求。

具体参考如下代码(C语言实现):

/*
 * ===============================================
 *
 *       Filename:  SearchSum.cpp
 *
 *    Description:  Θ(nlgn) 
 *
 *        Version:  1.0
 *        Created:  03/14/2010 10:56:55 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  timebug 
 *        Company:  
 *
 * ===============================================
 */

#include <stdio.h>

int Partition(int S[],int p,int r)
{
    int x=S[r];
    int i=p-1,j,temp;
    for(j=p;j<=r-1;++j){
        if(S[j]<=x){
            i++;
            temp=S[j];
            S[j]=S[i];
            S[i]=temp;
        }
    }
    temp=S[r];
    S[r]=S[i+1];
    S[i+1]=temp;
    return i+1;
}

void QuickSort(int S[],int p,int r)
{
    if(p<r){
        int q=Partition(S,p,r);
        QuickSort(S,p,q-1);
        QuickSort(S,q+1,r);
    }
}

int BinarySearch(int S[],int low,int high,int value)
{
    if(low>high)
        return -1;
    int mid=low+(high-low)/2;
    if(S[mid]>value)
        return BinarySearch(S,low,mid-1,value);
    else if(S[mid]<value)
        return BinarySearch(S,mid+1,high,value);
    else return mid;
}

void SearchSum(int S[],int n,int x)
{
    int i,temp,flag;
    for(i=1;i<=n;++i){
    int y=x-S[i];
    flag=1;
    temp=S[i];
    S[i]=y+1;
    if(BinarySearch(S,1,n,y)!=-1){
        printf("Yes\n");
        flag=0;
        break;
    }
    S[i]=temp;
    }
    if(flag) printf("No\n");
}

int main()
{
    int n=10,x;
    while(scanf("%d",&x)!=EOF){
        int S[]={0,33,7,6,7,3,5,42,1,11,10};
        QuickSort(S,1,n);
        printf("\n");
        SearchSum(S,n,x);
    }

    return 0;
}

posted @ 2010-03-13 23:45  timebug  Views(2968)  Comments(2Edit  收藏  举报