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

递归

一.递归三大要素

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(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;
}
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值