一图理解递归-算法通关村

一图理解递归-算法通关村


  • 递归是我们算法进阶的基础,是必须要掌握的内容,只有掌握了递归才算真的会算法。与递归有关的问题有:
    • 与树和二叉树相关的大部问题
    • 二分查找相关的问题
    • 快速排序、归并排序相关的问题
    • 所有回溯的问题
    • 所有动态规划的问题

1 递归的特征

  • 递归,大部分人都知道怎么回事,但是代码就是写不出来,所谓”你讲的都对,但我就是不会“。
    递归的本质仍然是方法调用,不过是自己调用自己,系统给我们维护了不同调用之间的保存和返回等功能。
    这种例子在现实中也有很多的,例如有一个笑话:
    从前啊,有座山,山上有座庙,庙里有个老和尚和一个小和尚在讲故事,老和尚对小和尚说:
    从前啊,有座山,山上有座庙,庙里有个老和尚和一个小和尚在讲故事,老和尚对小和尚说:

  • 如果看递归代码的结构,就像下面这个样子,前面的每一层都去一模一样地调下一层,不同的只是输入和输出的参数。

  • 当然这个过程不能一直持续下去,一定要在满足某个要求之后返回结果的,否则的话,就会抛出“StackOverFLow”的问题。

  • 所有的递归有两个基本的特征:
    执行时范围不断缩小,这样才能触底反弹
    终止判断在调用递归的前面,这个终止条件,就是要触的底。
    理解这几条特征可以辅助我们更好的理解递归,我们一条条来看:

    • 【1】 执行范围不断缩小
      递归就是数学里的递推,设计递归就是努力寻找数学里的递推公式,例如阶乘的递推公式就是f(n) n*f(n 1),很明显一定是要触底之后才能反弹。再比如斐波那契数列的递归公式为f(n)=f(n-1)+f(n-2),n也在不断缩小。这条规律可以辅助我们检查自己写的递推公式对不对。
      范围缩小不一定只体现n的变化上,在树的递归中我们会大量使用类似这样的结构:

    • 每递归一次,都将范围缩小到当前节点的左子树或右子树,范围也是在不断缩小。

    • int leftDepth = getDepth(node.left); int rightDepth = getDepth(node.right);
    • 【2】终止条件判断在递归调用的前面
      递归之后可能还有终止条件,但是在执行递归之前,一定会有一个终止条件。这一条也可以帮助我们检查自己写的算法对不对。


2 如何写递归

  • 明白了上面的道理,那么该怎么才能写出递归方法呢?
    第一步:从小到大递推
    大部分从n=1,2,3或者只有一两个元素开始写最简单。例如斐波那契序列为1 1 2 3 5 8, 从n=3开始都满足f(n) = f(n-1) + f(n-2),然后我们再选择某个比较大的n来验证即可。
    我们仍然以阶乘和斐波那契数列为例来看。斐波那契数列的是这样一个数列:1、1、2、3、5、8、13、21、34…即第一项 f(1) = 1,第二项 f(2) = 1⋯,从第三项开始就满足:
    f(n) = f(n-1) + f(n-2)
    这就是我们要找的递归公式。
    对于阶乘也是一样的:

  • n=1 f(1)=1 n=2 f(2)=2 ** f(1)=2 n=3 f(3)=3* * f(2)=6 n=4 f(4)=4 * f(3)=24
  • 由此我们可以推测出递推公式:f(n) = n * f(n-1)。

  • 第二步:分情况讨论,明确结束条件
    我们说过递归里终止条件一定是靠前的,而大部分递归的终止条件不过是n最小开始触底反弹时的几种情况。
    对于阶乘,当n=1时你就应该知道f(1)=1,也就是下面这样子:

  • //算 n 的阶乘(假设n不为0)
    int f(int n){
    if(n == 1){
    return 1;
    }
    }
  • 有时候需要考虑的终止条件不止一个,例如斐波那契数列的递推公式f(n)=f(n-1)+f(-2)里,如果n=2时会出现f(2)=f(1)+f(0),很明显这里是没有f(0)的,所以我们要将n==2也给限制住,所以结束条件是这样的:

  • int f(int n){
    if(n <= 2){
    return 1;
    }
    }
  • 有些情况不一定是触底才开始反弹,而是达到某种要求就要停止,这样需要考虑的情况会比较多。解决这类问题最直接的方式就是枚举,将可能的情况列举一下,再逐步优化。
    只有列举清楚了才可能将终止条件写完整,所以在面试的时候千万不要上来就写,而应该先和面试官讨论你的设计方案,不要害怕与面试官讨论!假如有明显的缺陷他甚至会提醒你的,所以这也是借力打力的一个技巧。

  • 第三步:组合出完整方法
    将递推公式和终止条件组合起来,变成完整的方法。

  • 算 n 的阶乘:

  • int factorial(int n){
    if(n == 1){
    return 1;
    }
    return factorial(n-1)*n
    }
  • 斐波那契数列的实现:

  • int fibonacci(int n){
    //1.先写递归结束条件
    if(n <= 2){
    return 1;
    }
    return fibonacci(n-1) + fibonacci(n-2);
    }

3 怎么看懂地柜代码

  • 对于很多人来说,特别是刚开始学习递归的时候,最大的问题是给了答案也看不懂。另外因为调试的时候断点位置的数值一直变,让人看着晕,因此递归的代码也贼难调试。
  • 我们先思考一个问题,上面的阶乘,如果n=4会调几次上面的 factorial(int n) 方法呢?很明显应该是4次,递归的特征就是“不撞南墙不回头”,n=4,3和2时会继续递归,而n=1时发现满足退出条件了,就执行 return 1,不再递归,而是不断返回上一层并计算。
  • 接着再看返回时每层参数的问题,递归本质上仍然是方法调用,所以可以按照方法调用的方式来验证写的对不对。
    下面这个图完整的表示了求阶乘的过程,你会发现递归不过是一个方法被调了好几次,每次n都在减小,这就是递进的过程。触底之后,也就是满足终止条件之后就开始返回了。
    递进的时候当前层的n被系统给保存了,而返回的时候会自动设置回来,因此每层的n自然是不一样的,所以此时就是重新拿到当前这一层n的值完成计算即可。
    例如我们将f(4)阶乘的过程如下:
    底之后,也就是满足终止条件之后就开始返回了。
    递进的时候当前层的n被系统给保存了,而返回的时候会自动设置回来,因此每层的n自然是不一样的,所以此时就是重新拿到当前这一层n的值完成计算即可。
    例如我们将f(4)阶乘的过程如下:

相关推荐

  1. 算法通关第十关—理解位运算的规则(青铜)

    2024-03-24 17:14:02       55 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-24 17:14:02       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-24 17:14:02       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-24 17:14:02       82 阅读
  4. Python语言-面向对象

    2024-03-24 17:14:02       91 阅读

热门阅读

  1. Oracle to_char可以转换哪些类型的数据

    2024-03-24 17:14:02       39 阅读
  2. 2.windows ubuntu子系统配置

    2024-03-24 17:14:02       41 阅读
  3. 2023天梯赛

    2024-03-24 17:14:02       41 阅读
  4. 长链接与短链接的理解

    2024-03-24 17:14:02       33 阅读
  5. Mockito.when返回的list长度为0问题解决方法

    2024-03-24 17:14:02       35 阅读
  6. 用I/O口模拟IIC总线协议遇到的一些问题

    2024-03-24 17:14:02       36 阅读
  7. 对原型模式的理解

    2024-03-24 17:14:02       47 阅读
  8. XXL-JOB通过Postman调试本地任务

    2024-03-24 17:14:02       42 阅读
  9. 对象与继承

    2024-03-24 17:14:02       40 阅读
  10. 【R包开发:包的组件】 第4章 包的元数据

    2024-03-24 17:14:02       35 阅读
  11. PTA-6-16 删除单链表的重复结点

    2024-03-24 17:14:02       39 阅读
  12. 深入浅出:大型语言模型(LLM)的全面解读

    2024-03-24 17:14:02       48 阅读
  13. DOcker in Docker 原理与实战代码详解

    2024-03-24 17:14:02       36 阅读