***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************
这个周末家里有点事,回了趟家,就断了一些学习计划。。
抓紧补上!
第一个算法——递归与分治
都知道,分治算法基本思想是
将一个难以直接解决的问题,分割成一些规模小的相同问题,以便各个击破,分而治之,
这样,我们就可以将一个复杂的算法,类型不变,规模越来越小,最终使子问题容易求解,由此引出递归算法。
其实,分治和递归像一对孪生兄弟,经常可以同时使用在算法设计之中,并由此产生许多高效的算法。
一、递归
概念:直接或间接的调用自身的算法称为递归算法,用函数自身给出定义的函数称为递归函数。
下面列出几个递归的经典例子:
① 阶乘
定义:
n!等于 1 当n=0
等于n(n-1)! 当n>0
int factorial(int n) { if( n == 0 ) return 1; return n*factorial(n-1); }
一个无穷的数列,序列为:1,1,2,3,5,8,13,21,34,55....
定义为:
F(n)= 1 当n=0
=1 当n=1
=F(n-1)+F(n-2) 当n>1
<span style="font-size:18px;">int fibonacci(int n) { if( n <= 1 ) return 1; return fibonacci(n-1)+fibonacci(n-2); }</span>
这是一个双递归函数,当一个函数及它的一个变量由函数自身定义时,称这个函数为双递归函数。
Ackerman函数A(n,m) 有两个独立的整型变量m≥0,n≥0,其定义如下:
A(1,0) = 2 m≥0
A(0,m) = 1 n≥2
A(n,0) = n+2 n≥2
A(n,m) = A(A(n-1,m),m-1) n,m≥1
int Ackerman( int n , int m ) { if( n==1 && m==0 ) return 2; if( n==0 ) return 1; if( m==0 ) return n+2; return Ackerman( Ackerman(n-1,m),m-1); }
④排列问题
对n个元素进行全排列,
当n=1时,Perm(P)=(r),其中r是集合R中唯一的元素
当n>1时,Perm(P)由(r1)Perm(R1),(r2)Perm(R2),.......,(rn)Perm(Rn)构成
template< class Type > inline void Swap(Type& a,Type& b) { Type temp = a; a = b; b = temp; } void Perm(Type list[],int k ,int m ) { if( k==m ) { for( int i = 0 ; i <= m; ++i ) cout<<list[i]; cout<<endl; } else { for( int i = k ; i <= m ; ++i ) { Swap(list[k],list[i]); Perm(list,k+1,m); Swap(list[k],list[i]); } }
将正整数n表示成一系列正整数之和,
n=n1+n2+...+nk(其中n1≥n2≥n3≥....≥nk)
例如,对于整数6的划分:
6
5+1
4+2 4+1+1
3+3 3+2+1 3+1+1+1
2+2+2 2+2+1+1 2+1+1+1+1
1+1+1+1+1+1
总共11种划分
按照最大加数n1不大于m的划分个数记作q(n,m),可以建立如下递归关系:
<1> q(n,1) = 1 , n≥1—— 当最大加数n1不大于1时,任何正整数n只有一种划分形式
<2> q(n,m)=q(n,n),n≥m—— 最大加数n1实际上不能大于n。因此q(1,m)=1
<3> q(n,m)=q(n,m-1)+q(n-m,m),n>m>1—— 正整数n的最大加数n1不大于m的划分由n1=m的和n1≤m-1的划分组成。
综上所述,可以总结出:
q(n,m)=
1 n=1,m=1
q(n,n) n<m
1+q(n,n-1) n=m
q(n,m-1)+q(n-m,m) n>m>1
int q( int n , int m ) { if( (n<1) || (m<1) ) return 0; if( (n==1) || (m==1) ) return 1; if( n==m ) return q(n,m-1)+1; return q(n,m-1)+q(n-m,m); }
⑥Hanoi塔问题
非常经典的一个递归问题,问题就不赘述了,直接写算法:
void hanoi( int n , int a , int b , int c ) { if( n > 0 ){ hanoi( n-1,a,c,b); move(a,b); hanoi( n-1,c,b,a); } }
二、分治
之前也说过,分治法的基本思想就是,把一个规模大的问题,不断分成相互独立且与原问题相同的小规模问题。
它的算法设计模式如下:
divide-and-conquer(P) { if( |P| <= n0 ) adhoc(P); divide P into smaller subinstances P1,P2,P3,...,Pk; for( i = 1 ; i <= k ; ++i ) yi = divide-and-conquer(Pi); return merge(y1,y2,...,yk); }
|P| 表示问题P的规模,
n0 为一阙值,表示当前问题P的规模不超过n0时,问题容易求解,不必再继续分解。
adhoc(P) 是该分治法中的基本子算法,对于容易求解的问题可以直接求解
merge(y1,y2...yk) 是合并子算法,用于将P的子问题P1,P2,...,Pk的解y1,y2,...,yk合并为P的解
分治法的计算效率通常用递归方程来分析,对于上述设计模式,解|P|=n的问题所需的计算时间,有:
T(n) = O(1) 当n=1
kT(n/m)+f(n) 当n>1
以上就是递归和分治的一些基本思想和原则,接下来的博文会有具体例子来说明。
***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************