数据结构与算法(3)——递归

递归是很多算法都会使用的基本编程方法,在本章中我们将会学习递归,并学习如何将问题分为基线条件和递归条件。

假设圣诞老人送给你了一个大大大大大箱子,然后你发现里面全都是礼物盒,你欣喜若狂略怀有期待打开第一个盒子,发现里面还是盒子……

你才理解过来自己被耍了。但是送礼物的人肯定不会这么无聊,他肯定在这其中某个盒子中藏了一个小小的礼物。

接下来,你要试图从这一堆盒子中找到自己的礼物。

你想到了两种办法。一种是把所有没有打开的盒子放在一个区域内,每次打开一个,如果发现里面是空的,就把它扔掉,如果里面是另一个盒子,就把里面那个盒子扔回到“盒子区域”,如果里面是礼物,那么正如你所愿。

第二个方法显得有些一根筋。你一次只打开一个盒子,然后里面如果还是盒子你就继续打开,直到空了才换另一个。

然而第二种这种看似耿直的方法就是递归,也就是函数调用自己。

假设打开盒子是一个函数所导致的行为,然后打开里面的盒子也是这个函数的行为,是这个函数使用了他自己去打开里面的盒子,也就是我们所说的“套娃”。

虽然这两种方法目的相同,但是在不考虑性能的情况下,递归明显要更清晰,也就是更容易理解。

当然,选择哪种方法,需要具体情况具体考虑。

但是如果使用了递归函数,那么函数应该在什么时候调用自己呢?又该在什么时候停止呢?

这个时候,我们需要引入基线条件和递归条件。递归条件就是函数调用自己的条件,基线条件就是函数不再调用自己,从而避免无限循环。

在盒子问题里,盒子里面空了就是基线条件,因为如果盒子里面空掉了你就没有盒子可以打开从而你就没有办法再调用“打开盒子”这个动作。相对的,里面还有盒子就是这个问题里的递归条件了。

现在你打开了第一个盒子,发现里面还是盒子,你只能把第一个盒子继续开着,然后去打开第二个盒子。这里给你一个问题,就是这个大盒子里到底有没有礼物?

你现在完全不能下定论,因为你还没有打开这个盒子里的全部盒子。如果对每个盒子都提一下这个问题,那么这个问题的结果将由下一个盒子决定。

就相当于,我们打开第一个盒子,哦里面还有一个,那现在让我们把这个问题放一放,先解决第二个盒子里有没有礼物这个问题。

让我们来把这个问题形象化一些。要解决第一个盒子里有没有礼物,需要解决第二个盒子才行。这样我们把这个问题放在最底下,解决第二个盒子才解决第一个,以此类推。

这样就形成了一个结构,栈。而我们添加问题的过程,又叫压入。读取并删除(也就是解决了这个问题)的过程,又叫弹出。

这一系列的操作又称为调用栈。

当然,这个问题的函数只有一个,所以看起来相当简单。

我们如果在一个函数中调用了其他函数,也同样这么操作:将第一个函数放到最底下,然后等其他函数运行完了我们再运行第一个函数。当然如果其他函数里还有别的函数那么如法炮制就行了。

回到最开始的两种方法,你会发现两种方法最明显的区别就是第二种没有创建盒子区域。

但是,当你引入调用栈这个概念的时候,你会发现把这些问题一个个压入栈后,盒子区域就是栈本身!因为每个问题就代表一个盒子,直到最后一个盒子为止,之前全部的盒子在一起就是所谓的盒子区域,不过换了种方式存在而已。

本章小结:

递归指的是调用自己的函数。
每个递归函数都有两个条件:基线条件和递归条件。
栈有两种操作:压入和弹出。
所有函数调用都进入调用栈。
调用栈可能很长,这会占用大量的运行内存。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值