《算法导论》[第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; }