作者 | 浮世万千吾爱有三

责编 | Carol

来源 | CSDN 博客

递归函数的理解

1、生活中的递归

“递归”在生活中的一个典例就是“问路”。如图小哥哥进入电影院后找不到自己的座位,问身边的小姐姐“这是第几排”,小姐姐也不清楚便依次向前询问,问至第一排的观众后依次向后反馈结果,“我是第一排”,“我是第二排”,···,最终确定自己座位所在排数。

在这个过程中充分反应了“传递”(询问)和“回归”(反馈)的思想,故将这种现象称为“递归”。

2、编程中的递归

计算机有两个特点:“很笨”又“很快”。所以将“复杂问题”转化为“多步骤的简单问题”后,计算机才能高效执行。

而递归是编程算法的一种,通过调用自身,将一些复杂的问题简单化,便于得出解决方案。

下面通过简单的案例了解编程中的递归

案例:计算1+2+3+4+5+6的和。

function fn(n){if(n === 1){return 1;}return n + fn(n - 1);
}
fn(6);

计算过程:

f(6) = 6 + f(5)

f(6) = 6 + 5 + f(4)

f(6) = 6 + 5 + 4 + f(3)

f(6) = 6 + 5 + 4 + 3 + f(2)

f(6) = 6 + 5 + 4 + 3 + 2 + f(1)

f(6) = 6 + 5 + 4 + 3 + 2 + 1

由上可知递归函数的本质

  • 调用自身

递归函数的实现有两个要素:

  1. 终止条件

  2. 逐步靠近终止条件

案例中的终止条件是:当 n === 1 时,fn(1) === 1。若没有终止条件,函数会继续计算f(0) 、f(-1) 、f(-2) ··· 从而进入死循环,无法得出结果。

通过计算过程可以看出,函数依次计算f(6)、f(5)、f(4)、f(3)、 f(2)、f(1),很好的满足了第二个要素 逐步靠近终止条件。

递归函数的使用

通过以上讲解,想必已经了解递归函数的原理,

那么递归函数是如何写出来的呢?

如何利用递归函数解决实际问题呢?

  • 实例探索递归函数的书写“套路”

例题计算n的阶乘

步骤 1找到终止条件,写给 if

数学知识 :n! = n * (n - 1) * (n - 2) * (n -3) * ··· * 3 * 2 * 1

显然 终止条件 是 n === 1; 时, return 1;

故可以完成函数的 前半部分:

function fn(n){if(n === 1){return 1;}// 未完待续
}

步骤2:找到函数的等价关系式,写给 return

数学知识 :n! = n * (n - 1)!

通过数学知识 n! = n * (n - 1)! 和递归函数的本质(调用自身),
可以得出函数的等价关系式为 f(n) = n * f(n - 1);

从而可以完成函数的后半部分:

function fn(n){if(n === 1){return 1;}return n * fn(n - 1);
}

至此简单的递归函数便写出来了,递归函数最大的特点便是代码简洁(简洁到让人心虚)。

总结,递归函数的书写“套路”

1.找到终止条件,写给 if

2.找到函数的等价关系式,写给 return

递归函数的问题

想必你会说,上面的两个例题用 循环 就能轻松写出来,为何还需要使用递归呢?

其实能用 递归 解决的问题,用 循环 也能解决!而且 递归 比 循环 的运算速度要慢,因为 递归 需要逐层调用函数,占据系统内存,当 递归 层级较深时,对性能消耗较大,往往不推荐使用。

问:那递归存在的意义是什么?

递归 是为了将复杂问题简单化,提供解题思路,进而得到 “循环算法”

对于简单问题,一眼便能看出“循环算法”,但对于抽象问题,通常可以先采取 递归 思想,如:

例题:某人需要走上10级台阶,有两种走法,走法A:一步1个台阶;走法B:一步2个台阶。两种走法可以任意交替使用,问走上10级台阶共有多少种方法?

这个问题很难直接看出循环的解题思路,我们不妨从 递归 的角度尝试解决:

当走上第10级台阶只差最后一步时,存在有两种可能:
第1种:从 第8级 —> 第10级(一步2个台阶)
第2种:从 第9级 —> 第10级(一步1个台阶)

假设:从 第0级 —> 第8级,有 x 种走法;

1,1,1,1,1,1,2,

1,1,1,1,1,2,1,

1,2,1,1,1,2,

1,2,1,2,2,

·······

// 穷举不尽,共 x 种,每种走法的最后一步都是 2(个台阶)

假设:从 第0级 —> 第9级,有 y 种走法;

1,1,1,1,1,1,1,2,

1,1,2,1,1,2,1,1

1,2,1,2,2,1,

1,2,2,2,2,

·······

// 穷举不尽,共 y 种,每种走法的最后一步都是 1(个台阶)

那么,从 第0级 —> 第10级,共有 x + y 种走法。

故,10级台阶走法 = 9级台阶走法 + 8级台阶走法,即 f(10) = f(9) + f(8);

所以我们需要的函数关系式是 f(n) = f(n - 1) + f(n - 2);

接下来找 终止条件:

1级台阶时,走法只有1种(1步1台阶),是 n === 1; 时, return 1;
2级台阶时,走法只有2种(2次1步1台阶 或 1步2台阶),是 n === 2; 时, return 2;

由此可以写出递归函数

function fn(n){if(n === 1 || n === 2){return n;}return fun(n - 1) + fun(n - 2);
}

下图展示了函数的执行过程

可见,在函数执行过程中重复调用了多次相同的函数(相同背景色),从而极大消耗了系统的性能。经过测试这个 递归函数 最多可计算至 f(45);左右的结果(测试需谨慎),这便是 递归函数 存在的主要问题。

那么如何优化这个问题呢?

即,将 递归算法改为循环算法。

通过前面的推导我们知道 f(n) = f(n - 1) + f(n - 2);

1级台阶 ==> 走法:1

2级台阶 ==> 走法:2

3级台阶 ==> 走法:1 + 2 = 3

4级台阶 ==> 走法:2 + 3 = 5

5级台阶 ==> 走法:3 + 5 = 8

6级台阶 ==> 走法:5 + 8 = 13

7级台阶 ==> 走法:8 + 13 = 21

······

即,只要知道前两项(1级台阶和2级台阶)的结果,就可以自底向上依次推算出后面所有项的结果。

于是便可以写出 循环函数

function fn(n){if(n === 1 || n === 2){return n;}var left = 1; // 左边的数据var right = 2; // 右边的数据var sum = 0;for(var i = 3 ; i <= n ; i++){ // 循环从第3项开始sum = left + right; // 计算前一次左右数据的和left = right; // 把前一次的right赋值给下一次的leftright = sum; // 把前一次的和赋值给下一次的right}return sum;
}

以上便是通过递归思想将抽象问题逐步简单化,从而得出循环算法的过程。
循环算法 解决了 递归 消耗系统性能的问题,可以计算任意数值。

(当数值太大超出JavaScript数值范围时,返回 Infinity

总结

1、递归结构简单,易理解,常用于将抽象问题简单化。

2、递归要有终止条件,否则会变成死递归;

3、递归算法运行效率低、性能消耗大,递归深度较大时慎用(等不到结果);

4、能用递归解决的问题大多都能用循环解决。

【end】

原力计划

《原力计划【第二季】- 学习力挑战》正式开始!即日起至 3月21日,千万流量支持原创作者!更有专属【勋章】等你来挑战

推荐阅读

  • 赔偿谷歌1.8亿美元!前Uber自动驾驶主管被告到破产

  • “一网打尽”Deepfake等换脸图像,微软提出升级版鉴别技术Face X-Ray

  • 小米回应"暴力裁员";报告称安卓手机贬值速度是 iPhone 两倍;Ant Design 4.0.1 发布

  • 时间复杂度的表示、分析、计算方法……一文带你看懂时间复杂度!

  • 闪电网络的 5 个优点和4 个缺点、本质、来源与工作原理……一文带你读懂闪电网络!

  • 国内外大厂集结,远程办公大考成绩单发布!

  • 你点的每个“在看”,我都认真当成了AI

Javascript函数之深入浅出递归思想,附案例与代码!相关推荐

  1. PHP通过函数来了解字符串格式化的案例和代码

    目录 前言 一.字符串空格的删除 1.tri()函数 2.ltrim()函数 3.rtrim()函数 二.字符串大小写转换 1.strtolower()函数 2.strtoupper()函数 三.格式 ...

  2. js 判断变量是否有值返回bool_有没有办法可以获得javascript函数返回值里的bool值,代码如下:...

    functionsubmitClick(){if(document.getElementById("userID").value==""){document.g ...

  3. Python中的匿名函数及递归思想简析

    匿名函数 前言 很多人学习python,不知道从何学起. 很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手. 很多已经做案例的人,却不知道如何去学习更加高深的知识. 那么针对这三类 ...

  4. JavaScript函数之实际参数对象(arguments) / callee属性 / caller属性 / 递归调用 / 获取函数名称的方法...

    函数的作用域:调用对象 JavaScript中函数的主体是在局部作用域中执行的,该作用域不同于全局作用域.这个新的作用域是通过将调用对象添加到作用域链的头部而创建的(没怎么理解这句话,有理解的亲可以留 ...

  5. python匿名函数的作用_Python中的匿名函数及递归思想简析

    匿名函数 前言 上次咱们基本说了一下函数的定义及简单使用,Python中的基本函数及其常用用法简析,现在咱们整点进阶一些的.同样都是小白,咱也不知道实际需要不,但是对于函数的执行顺序以及装饰器的理解还 ...

  6. ----斐波那契数列---eval函数----类递归思想 栈 进出 思想

    ------------ 斐波那契 数列 --------------- [1,1,2,3,5,8,13,21,34,...] 1 列表方法实现 # l=[1,1] # # # while len(l ...

  7. 匿名函数自我调用_Python中的匿名函数及递归思想简析

    匿名函数 前言 上次咱们基本说了一下函数的定义及简单使用,Python中的基本函数及其常用用法简析,现在咱们整点进阶一些的.同样都是小白,咱也不知道实际需要不,但是对于函数的执行顺序以及装饰器的理解还 ...

  8. C++两个函数可以相互递归吗_通俗讲:数据结构递归思想

    通俗讲:数据结构递归思想 脑容量有限,拒绝花里胡哨 一个递归求阶乘的例子 #如5的阶乘 f(6)=6*5*4*3*2*1 def f(int n) {if n <= 0 : return 1re ...

  9. Python中匿名函数及递归思想简析(小本本记下来)

    前言: 上次咱们基本说了一下函数的定义及简单使用,想要了解的伙伴可以去看看.Python中最基本的函数及其常用用法简析(新手必备)小本本记起来 现在咱们整点进阶一些的.同样都是小白,咱也不知道实际需要 ...

最新文章

  1. 网页制作之html基础学习3-css样式表
  2. 批量修改mp3文件的title等
  3. 阿里巴巴为什么要禁止使用存储过程?
  4. gdiplus 水印_Delphi程序的应用GDI+制作水印效果图片
  5. 如何为 .NET Core 3.0 中 WPF 配置依赖注入 ?
  6. 天津大学计算机预推免机试_2019预推免汇总 | 9.18New!
  7. 【英语学习】【Daily English】U15 Culture L04 When in Rome, do as the Romans Do
  8. git 简单使用 基本操作
  9. java集合输入存储_Java练习IO流使用Properties集合存储数据并...
  10. CSS按钮动画(三)
  11. web压力测试的几个指标
  12. 曼联队选择Tezos作为官方区块链和训练工具包合作伙伴
  13. 网易交互设计师微专业 C1 揭开交互神秘面纱
  14. 计算机系统集成工作总结,系统集成工作总结报告.docx
  15. sub 对应php什么函数,subtotal函数的使用方法1-9分别什么意思
  16. 仿真软件测试基尔霍夫定律,标签:基尔霍夫定律
  17. 计算机科学与技术社会实践活动,计算机科学与技术学院学生开展“食品安全行”社会实践活动...
  18. ajax 返回xml 怎么显示显示图片,如何使用jquery和ajax读取,解析和显示xml
  19. bat文件打开cmd指向某个目录,并执行命令
  20. 关于计算机科学的课题,计算机与科学论文选题推荐 计算机与科学论文题目如何定...

热门文章

  1. 绿色信托任重道远 应建立补偿机制?
  2. oracle 9i 安装及连接远程数据库
  3. Ubuntu“无法获得锁\加锁”解决方案
  4. 关于手机已处理里重复单据的处理办法
  5. 求教关于NFS服务器的防火墙的设置
  6. ajax合并表单,ajax。表单
  7. python 图像处理模块pillow
  8. [模拟]纺车的轮子 Spinning Wheels
  9. js绑定事件和解绑事件
  10. Oracle11g密码忘记处理方法