Javascript函数之深入浅出递归思想,附案例与代码!
作者 | 浮世万千吾爱有三
责编 | 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
由上可知递归函数的本质:
调用自身
递归函数的实现有两个要素:
终止条件
逐步靠近终止条件
案例中的终止条件是:当 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,2
1,1,1,1,1,2,1,2
1,2,1,1,1,2,2
1,2,1,2,2,2
·······
// 穷举不尽,共 x 种,每种走法的最后一步都是 2(个台阶)
假设:从 第0级 —> 第9级,有 y
种走法;
1,1,1,1,1,1,1,2,1
1,1,2,1,1,2,1,1
1,2,1,2,2,1,1
1,2,2,2,2,1
·······
// 穷举不尽,共 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函数之深入浅出递归思想,附案例与代码!相关推荐
- PHP通过函数来了解字符串格式化的案例和代码
目录 前言 一.字符串空格的删除 1.tri()函数 2.ltrim()函数 3.rtrim()函数 二.字符串大小写转换 1.strtolower()函数 2.strtoupper()函数 三.格式 ...
- js 判断变量是否有值返回bool_有没有办法可以获得javascript函数返回值里的bool值,代码如下:...
functionsubmitClick(){if(document.getElementById("userID").value==""){document.g ...
- Python中的匿名函数及递归思想简析
匿名函数 前言 很多人学习python,不知道从何学起. 很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手. 很多已经做案例的人,却不知道如何去学习更加高深的知识. 那么针对这三类 ...
- JavaScript函数之实际参数对象(arguments) / callee属性 / caller属性 / 递归调用 / 获取函数名称的方法...
函数的作用域:调用对象 JavaScript中函数的主体是在局部作用域中执行的,该作用域不同于全局作用域.这个新的作用域是通过将调用对象添加到作用域链的头部而创建的(没怎么理解这句话,有理解的亲可以留 ...
- python匿名函数的作用_Python中的匿名函数及递归思想简析
匿名函数 前言 上次咱们基本说了一下函数的定义及简单使用,Python中的基本函数及其常用用法简析,现在咱们整点进阶一些的.同样都是小白,咱也不知道实际需要不,但是对于函数的执行顺序以及装饰器的理解还 ...
- ----斐波那契数列---eval函数----类递归思想 栈 进出 思想
------------ 斐波那契 数列 --------------- [1,1,2,3,5,8,13,21,34,...] 1 列表方法实现 # l=[1,1] # # # while len(l ...
- 匿名函数自我调用_Python中的匿名函数及递归思想简析
匿名函数 前言 上次咱们基本说了一下函数的定义及简单使用,Python中的基本函数及其常用用法简析,现在咱们整点进阶一些的.同样都是小白,咱也不知道实际需要不,但是对于函数的执行顺序以及装饰器的理解还 ...
- C++两个函数可以相互递归吗_通俗讲:数据结构递归思想
通俗讲:数据结构递归思想 脑容量有限,拒绝花里胡哨 一个递归求阶乘的例子 #如5的阶乘 f(6)=6*5*4*3*2*1 def f(int n) {if n <= 0 : return 1re ...
- Python中匿名函数及递归思想简析(小本本记下来)
前言: 上次咱们基本说了一下函数的定义及简单使用,想要了解的伙伴可以去看看.Python中最基本的函数及其常用用法简析(新手必备)小本本记起来 现在咱们整点进阶一些的.同样都是小白,咱也不知道实际需要 ...
最新文章
- 网页制作之html基础学习3-css样式表
- 批量修改mp3文件的title等
- 阿里巴巴为什么要禁止使用存储过程?
- gdiplus 水印_Delphi程序的应用GDI+制作水印效果图片
- 如何为 .NET Core 3.0 中 WPF 配置依赖注入 ?
- 天津大学计算机预推免机试_2019预推免汇总 | 9.18New!
- 【英语学习】【Daily English】U15 Culture L04 When in Rome, do as the Romans Do
- git 简单使用 基本操作
- java集合输入存储_Java练习IO流使用Properties集合存储数据并...
- CSS按钮动画(三)
- web压力测试的几个指标
- 曼联队选择Tezos作为官方区块链和训练工具包合作伙伴
- 网易交互设计师微专业 C1 揭开交互神秘面纱
- 计算机系统集成工作总结,系统集成工作总结报告.docx
- sub 对应php什么函数,subtotal函数的使用方法1-9分别什么意思
- 仿真软件测试基尔霍夫定律,标签:基尔霍夫定律
- 计算机科学与技术社会实践活动,计算机科学与技术学院学生开展“食品安全行”社会实践活动...
- ajax 返回xml 怎么显示显示图片,如何使用jquery和ajax读取,解析和显示xml
- bat文件打开cmd指向某个目录,并执行命令
- 关于计算机科学的课题,计算机与科学论文选题推荐 计算机与科学论文题目如何定...