当前位置:   article > 正文

递归(详解+记忆化递归)_递归的记忆化

递归的记忆化

递归

一.递归三大要素

1.明确这个函数想要干什么
函数的功能;
2.寻找递归的结束条件
找到递归的参数为什么时,递归结束;
也就是递归的出口;
3.找出函数的等价关系式
不断缩小参数的范围,在递推中把找到子问题,递归解决掉子问题。

递归 = 自己调用自己 + “递归出口”

二.递归思想

所有递归的代码都类似,基本上都是有一个基线条件和一个递归条件;
基线条件(递归出口):指的是函数不在调用自己,从而避免无限循环;
递归条件(递归调用):函数调用自己;

伪代码如下:
int f(int n)
{
	if(递归结束条件)
		return//	基线条件;
	else
		return 递归;
						//递归条件;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

三.递归示例讲解

求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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

同时我们也知道阶乘是能够通过循环实现的
所以:
循环都可以换成递归来实现

四.递归的进阶版——记忆化递归

在递归的过程中我们可能会遇到计算重复的子问题,这个时候就需要用到记忆化递归了。

例如:
斐波那契数列
1 1 2 3 5 8 13 …

递推关系式:f(n) = f(n-1) + f(n-2);
递归的出口为:f(1) = 1, f(2) = 1;

例如f(6)的求解过程图如下:
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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/秋刀鱼在做梦/article/detail/916568
推荐阅读
相关标签
  

闽ICP备14008679号