一文弄懂递归

什么是递归?什么是递归回溯?如何使用递归?

注:本文默认对递归有一定了解,所以刚开始会 cover 一些简单的例子

一、何为递归

何为递归?程序反复调用自身即是递归

用数学代入法来理解就好。

假设我们用递归来算阶乘 f(n)

f(n) = n * f(n-1)

f 里面用到了 f, 怎么理解呢?

很简单,把式子展开即可:

f(6)
=> 6 * f(5)
=> 6 * (5 * f(4))
=> 6 * (5 * (4 * f(3)))
=> 6 * (5 * (4 * (3 * f(2))))
=> 6 * (5 * (4 * (3 * (2 * f(1)))))
=> 6 * (5 * (4 * (3 * (2 * 1))))
=> 6 * (5 * (4 * (3 * 2)))
=> 6 * (5 * (4 * 6))
=> 6 * (5 * 24)
=> 6 * 120
=> 720
看到递归了吗?

先递进,再回归——这就是「递归」。

二、递归的重要组成部分

明白了什么是递归之后,我们就要从一个抽象的层面上来对他抽丝剥茧,究竟什么构成了一个递归?

首先,要继续接下面的内容,我们需要引入一个概念——栈帧 (stack frame)。我们可以把栈帧简单理解成一层层的盒子,每当我们调用一次函数,关于该函数的调用以及返回地址就会被放到栈帧的顶上。拿阶乘举例,我们最后画出来的栈帧就是这样的:

Stack Frame

我们可以看到在栈帧的顶上是f(1),到这个时候我们就没有去计算所谓的f(0)了。这是因为我们现在到了一个递归的终止条件。顾名思义,当到这个地方时阶乘就不会继续往下,因为没有了意义。

第二步,我们要明白每一层要给上一层提供什么信息。继续看阶乘的算法,我们可以发现,每一层都会返回一个n * f(n-1)。其中这个就是我们留下来的信息,而这个信息就会被逐步返回,直到返回第一层。这也叫做 recursive case。也就是没有到终止条件时,递归会做什么

总的来说,有两个条件在递归中非常重要:

  • 递归的终止条件
  • 没有到终止条件时做的事情

三、递归的例子

接下来我们看一下如何真正的实现一个递归:

阶乘

首先,阶乘是递归的一个经典问题,因为我们已经发现了阶乘的递推的公式

f(n) = n * f(n-1)

所以我们很快就可以写出如下代码:

int factorial(int n){
    if(n == 0){
        return 1;
    }
    return n * factorial(n - 1);
}

简单分析一下,我们的递归结束条件就是 n == 0,因为在这里我们没有继续再调用自己往下算了。

接着,我们也实现了我们在递归时要做的东西,即n * f(n - 1)。可以想象一下,没有这一部分,我们是没有办法能够把这个阶乘问题划分成更小的子问题的。所以这一部分是必须有的。

所以,一个非常重要的点就是,你要确保你的函数在每次递归之后,都能够解决一点原来的问题。这也叫做问题的分解,这也是递归的精髓所在——将原问题不断拆分为与原问题等价的小问题

斐波那契数列

斐波那契数列的是这样一个数列 :1、1、2、3、5、8、13、21、34...., 即第一项 f(1) = 1, 第二项 f(2) = 1....., 第 n 项目为 f(n) = f(n-1) + f(n-2)。 求第 n 项的值是多少。

首先,

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值