简单略看了一遍王晓东的《计算机算法设计与分析》,很多地方没有细看,现在先做个小总结,方便以后回头看的时候记忆起一些内容。
第二章:递归与分治策略
递归的概念:
直接或间接地调用自身的算法称为递归算法。用函数自身给出定义的函数称为递归函数。
分治法的基本思想:
分治法的基本思想是将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相同。递归地解这些子问题,然后将各子问题的解合并得到原问题的解。它的一般的算法设计模式如下:
divide-and-conquer(P){
if(|P|<=n0)
adhoc(P);
divide P into smaller subinstances P1,P2,...,Pk;
for(i =1;i<=k;i++)
yi=divide-and-conquer(Pi);
return merge(y1,y2,...,yk);
}
其中,|P|表示问题P的规模,n0为一个阈值,表示当问题P的规模不超过n0时,问题已容易解出,不必再继续分解。adhoc(P)是该分治法中的基本子算法,用于直接解小规模的问题P。当P的规模不超过n0时,直接用算法adhoc(P)求解。算法merge(y1,y2,...,yk)是该分治法中的合并子算法,用于将P的子问题P1,P2,...,PK的解y1,y2,...,yk合并为P的解。
人们从大量实践中发现,在用分治法设计算法时,最好使子问题的规模大致相同。即将一个问题分成大小相等的K个子问题的处理方法是行之有效的。许多问题可以取K=2。
然后是递归分治的一些应用例子:
(1)二分搜索技术
若是给定n个排好序的元素。
假设现在要搜索x,对于存放在数组,每次先取a[n/2]与x比较。如果x=a[n/2],则找到x,算法终止;如果x<a[n/2],则只继续在[0,n/2-1]区间搜索;x>a[n/2]则在[n/2+1,n-1]搜索。很明显这个搜索过程与搜索BFS(二叉搜索树)类似。
(2)合并排序
基本思想:若要对n个元素进行排序(按增序排序,即从小到大),那么在n不等于1的时候(n>1),把n个元素分成大小相等的两份,分别对这两个子部分进行排序,然后对这两个子部分进行合并。当n=1的时候,明显不用再分了,只需要合并即可。主要的排序其实是发生在合并过程中。
算法大概这样:先说说合并,合并过程如下:给两个变量i、j,他们分别指向两个子部分a[n/2]、b[n/2]的起始元素,(这里就让i、j作为下标)。比如i=k时,a[i]代表是数组a中第k+1个元素。不断比较a[i]和b[j],小的那个放入临时数组temp[n],然后小的那个元素对应的变量往后移一位,如i++或j++,直至i=n/2或j=n/2。之后就把剩下没放进数组temp的元素按原来的顺序依次放进去即可。这个合并过程就是最后一趟合并,很明显如果想要这趟合并之后的temp数组就是排好序的数组的话,a[n/2]和b[/n]必须是排好序的。为了对这两个数组排序,可以将a[n/2]和b[n/2]再各自分解成规模相等的两个子数组c[n/4]、d[n/4]和e[n/4]、f[n/4],再分别合并。只有当n=1时,才可以直接合并,否则都是先分成更小的两个部分。所以就有了先分治。
下面给出简单实现代码:
void MergeSort(int *a,const int left,const int right){//合并排序,a是要排序的数组地址,left和right是要进行排序的范围
if (left >= right)
return;
int p = (left + right) / 2;
MergeSort(a, left, p);
MergeSort(a, p + 1, right);
Merge(a, left, right, p);
}
void Merge(int *a,const int left,const int right,const int p){//合并两个子数组
int i = left, j = p + 1;//他们各自的其实位置由left和p给出.用i、j作为他们各自的下标
int *temp = new int[right - left + 1](), m = 0;//开辟临时数组temp,m作为它的下标
while ((i < p + 1) &&