递归思想真的很好,但是递归的思想不是那么容易就想起来的,要找到问题的规律,若能够把这个问题化为和该问题类似的但是规模要小一些的问题,那就可以用递归的思想!当意识到要这个问题可以用递归来解决时,怎么来写出程序,又是一个挑战!通常遇到这种情况,个人认为讨论一般的情况,要比举特例要好很多。
递归通常把一个大型复杂的问题层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句 来定义对象的无限集合 。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
注意:
(1) 递归就是在过程或函数里调用自身;
(2) 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口
递归算法 一般用于解决三类问题:
(1)数据的定义是按递归定义的。(Fibonacci函数)
(2)问题解法按递归算法实现。(回溯)
(3)数据的结构形式是按递归定义的。(树的遍历,图的搜索)
递归的缺点: 递归算法解题的运行效率较低。在递归调用 的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。
下面讨论一个递归的例子:求全排列。
求全排列可以用递归的思路来解决,看下面一个特例:
a b c的全排列为:
a b c
a c b
b a c
b c a
c a b
c b a
由这个例子我们可以发现,a b c 的全排列为 a + (b c)的全排列,b+ (a c)的全排列, c+ (a b) 的全排列,这样一个较大规模的问题就转换成了一个较小规模的类似的问题,直到该问题规模小到只求一个字母的全排列时,我们就找到了上面说说的边界条件。
下面来讨论具体的程序是字母实现递归的,先把源代码贴出来:
void perm(char a[],int k,int n)
{
if(k==n)
{
for(int i=1;i<=n;i++)
cout<<a[i]<<" ";
cout<<endl;
}
else
{
for(int j=k;j<=n;j++)
{
char t;
t=a[j]; a[j]=a[k]; a[k]=t;
perm(a,k+1,n);
t=a[j]; a[j]=a[k]; a[k]=t;
}
}
}
思路是这样的,先从整体上来说,这个函数的作用就是求一个数组中存储的数据(0号未用),从第k个元素到第n个元素之间的元素的全排列!就是该函数就是求a[k]、a[k+1]、a[k+2]……a[n]的全排列。即为:
a[k] + (a[k+1]……a[n])的全排列
a[k+1]+(a[k]、a[k+2]……a[n])的全排列
a[k+2]+ (a[k]、a[k+1]……a[n])的全排列
……
a[n]+ (a[k]、a[k+1]……a[n-1])的全排列
注意上面的是将a[k]和a[i]换一下位置,从而满足该函数的定义,总是求第几个数到第几个数的全排列!
所以一个函数的作用就相当于n-k+1个函数的作用,这样我们就找到了递归的这种连接,使其有递归下去的可能性!
唉,说的我自己都乱了!多做下这方面的题,会有更好更深的理解!