赞
踩
1.明确这个函数想要干什么
函数的功能;
2.寻找递归的结束条件
找到递归的参数为什么时,递归结束;
也就是递归的出口;
3.找出函数的等价关系式
不断缩小参数的范围,在递推中把找到子问题,递归解决掉子问题。
递归 = 自己调用自己 + “递归出口”
所有递归的代码都类似,基本上都是有一个基线条件和一个递归条件;
基线条件(递归出口):指的是函数不在调用自己,从而避免无限循环;
递归条件(递归调用):函数调用自己;
伪代码如下:
int f(int n)
{
if(递归结束条件)
return ; // 基线条件;
else
return 递归;
//递归条件;
求n的阶乘:
我们都知道:
4的阶乘4!= 4 * 3 * 2 * 1;
5的阶乘5!= 5 * 4 * 3 * 2 * 1;
5的阶乘也就可以另写为5 * 4!;
所以通过递推,我们也就能知道n!= n * (n-1)!
①.n -> n-1就是我们找到的变化的量,作为递归的参数发生改变
②.而求n的阶乘最终也被分解成了求(n-1)的阶乘,这样更小的子问题,这也可以叫做是等价替换式:n! = n * (n-1)!
③.找出口:n一直会乘到1,所以1就是递归的出口
那么根据上述的讨论过程,我们就能够写出阶乘的代码:
int Factorial(int n)
{
if(n == 1)
return 1;
return n * f(n - 1);
}
int main()
{
int n, sum;
scanf("%d",&n);
sum = Factorial(n);
printf("%d",sum);
return 0;
}
同时我们也知道阶乘是能够通过循环实现的
所以:
循环都可以换成递归来实现
在递归的过程中我们可能会遇到计算重复的子问题,这个时候就需要用到记忆化递归了。
例如:
斐波那契数列:
1 1 2 3 5 8 13 …
递推关系式:f(n) = f(n-1) + f(n-2);
递归的出口为:f(1) = 1, f(2) = 1;
例如f(6)的求解过程图如下:
我们可以看到,f(4),f(3)…这些都被重复计算了多次,我们可以利用一个数组来记忆,当遇到计算过了值的时候,就直接返回计算的值就好了。
同时我们来讨论一下递归的过程:
当你要计算f(6)的时候就要先计算f(5),当你要计算f(5)的时候,要先计算f(4),当一直到第四步的f(2)的时候,就返回到上一层,然后计算第五步的f(1),又返回…
由此我们发现,递归的实现就是先向下,再向右;
和二叉树中的先序遍历很相似。
代码如下:
int f(int n,int a[]) { if(a[n]) return a[n]; return a[n] = f(n-1,a) + f(n-2,a); } int main() { int n, sum, a[50]; memset(a,0,sizeof(a)); scanf("%d",&n); a[1] = 1; a[2] = 1; sum = f(n,a); printf("%d",sum); return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。