当前位置: 首页 > 编程日记 > 正文

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

由上可知递归函数的本质

  • 调用自身

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

  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

相关文章:

Linux指令--文件和目录属性

对于每一个Linux学习者来说&#xff0c;了解Linux文件系统的目录结构&#xff0c;是学好Linux的至关重要的一步.&#xff0c;深入了解linux文件目录结构的标准和每个目录的详细功能&#xff0c;对于我们用好linux系统只管重要&#xff0c;下面我们就开始了解一下linux目录结构的…

Linux内存寻址

一.内存地址分类以及MMU介绍 对于程序员来说&#xff0c;可以简单的把内存地址理解为一种访问存储单元的内容的一种方式。而对于80x86系列微处理器来说&#xff0c;我们需要区分三种地址&#xff1a; &#xff08;1&#xff09;逻辑地址 这种地址通常使用在机器语言里用于指…

iptables 基本命令使用举例

原文地址&#xff1a;http://www.linuxsky.org/doc/admin/200803/262.html 一、链的基本操作 1、清除所有的规则。 1&#xff09;清除预设表filter中所有规则链中的规则。 # iptables -F 2&#xff09;清除预设表filter中使用者自定链中的规则。 #iptables -X #iptables -Z 2、…

重磅!教育部再次审批179所高校新增本科AI专业

整理&责编 | 夕颜出品 | CSDN&#xff08;ID:CSDNnews&#xff09;好消息&#xff01;2 月 21 日&#xff0c;教育部官网发布了《教育部关于公布 2019 年度普通高等学校本科专业备案和审批结果的通知》&#xff0c;公开了 2019 年度普通高等学校本科专业备案和审批结果。17…

Qt之自定义搜索框

简述 关于搜索框&#xff0c;大家都经常接触。例如&#xff1a;浏览器搜索、Windows资源管理器搜索等。 当然&#xff0c;这些对于Qt实现来说毫无压力&#xff0c;只要思路清晰&#xff0c;分分钟搞定。 简述效果细节分析Coding源码下载效果 细节分析 实现细节需要如下步骤&…

大型网站架构演变和知识体系

存爱好&#xff0c;作为收藏&#xff0c;原地址&#xff1a;http://www.blogjava.net/BlueDavy/archive/2008/09/03/226749.html&#xff0c;同时向原创致敬之前也有一些介绍大型网站架构演变的文章&#xff0c;例如LiveJournal的、ebay的&#xff0c;都是非常值得参考的&#…

Python数据清理终极指南(2020版)

作者 | Lianne & Justin译者 | 陆离出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;一般来说&#xff0c;我们在拟合一个机器学习模型或是统计模型之前&#xff0c;总是要进行数据清理的工作。因为没有一个模型能用一些杂乱无章的数据来产生对项目有意义的结果。…

内存地址转换与分段

原文标题&#xff1a;Memory Translation and Segmentation 原文地址&#xff1a;http://duartes.org/gustavo/blog/ 翻译地址&#xff1a;http://blog.csdn.net/drshenlei/article/details/4261909 本文是Intel兼容计算机&#xff08;x86&#xff09;的内存与保护系列文章的第…

c++ 普通高精减

//c 普通高精减 //codevs 3115 高精度练习之减法 //内容简单&#xff0c;就不注释了。 //注意下&#xff0c;&&优先级高于||。 #include<cstdio>#include<cstring>char s1[600],s2[600];int a1[600],a2[600],len1,len2,i;int main(){scanf("%s",…

腾讯提超强少样本目标检测算法,公开1000类检测训练集FSOD | CVPR 2020

作者 | VincentLee来源 | 晓飞的算法工程笔记不同于正常的目标检测任务&#xff0c;few-show目标检测任务需要通过几张新目标类别的图片在测试集中找出所有对应的前景。为了处理好这个任务&#xff0c;论文主要有两个贡献&#xff1a;提出一个通用的few-show目标检测算法&#…

Linux加入到Windows域 收藏

一、实验环境&#xff1a; AD server&#xff1a;windows server 2003samba&#xff1a;redhat as5AD server的hostname和IP地址&#xff1a;turbomai-c<?xml:namespace prefix st1 ns "urn:schemas-microsoft-com:office:smarttags" />89f91.test.com 192…

哈希函数原理及实现

哈希解决冲突 1000以内的素数 一般的hash实现已经总结出一些比较重要的素数&#xff1a; static unsigned int table_size[] {7,13,31, 61, 127, 251, 509, 1021,2039, 4093, 8191, 16381, 32749, 65521,1310…

基于Virtual DOM与Diff DOM的测试代码生成

尽管是在年末&#xff0c;并且也还没把书翻译完&#xff0c;也还没写完书的第一稿。但是&#xff0c;我还是觉得这是一个非常不错的话题——测试代码生成。当我们在写一些UI测试的时候&#xff0c;我们总需要到浏览器去看一下一些DOM的变化。比如&#xff0c;我们点击了某个下拉…

Win32 环境下的堆栈

原文已经找不到&#xff0c;作者应该是&#xff1a;http://blog.csdn.net/slimak 但是没有找到此文&#xff0c;其中丢了2幅图 简介 在Win32环境下利用调试器调试应用程序的时候经常要和堆栈(Stack)打交道,尤其是在需要手工遍历堆栈(Manually Walking Stack)的时候我们需要…

在VMWare中配置SQLServer2005集群 Step by Step(四)——集群安装

在VMWare 中配置集群 1. 进入command 命令窗口执行以下命令&#xff0c;创建仲裁磁盘和共享数据磁盘 vmware-vdiskmanager.exe -c -s 200Mb -a lsilogic -t 2 F:\VM\Share\Windows\SQLServer\quorum.vmdk vmware-vdiskmanager.exe -c -s 4Gb -a lsilogic -t 2 F:\VM\Share\Wind…

口罩检测识别率惊人,这个Python项目开源了

作者 | 一颗小树x&#xff0c;CSDN 博主编辑 | 唐小引来源 | CSDN 博客昨天在 GitHub 上看到一个有趣的开源项目&#xff0c;它能检测我们是否有戴口罩&#xff0c;跑起程序测试后&#xff0c;发现识别率挺高的&#xff0c;也适应不同环境&#xff0c;于是分享给大家。首先感谢…

CentOS搭建msmtp+mutt实现邮件发送

1&#xff1a;搭建配置msmtp下载msmtp包&#xff1a;官方地址&#xff1a;http://msmtp.sourceforge.net/download.html编译&#xff0c;安装(官方下载的包为tar.xz格式):#xz -d msmtp-1.6.3.tar.xz #tar -xvf msmtp-1.6.3.tar #cd msmtp-1.6.3 #./configure --prefix /opt/app…

Linux环境下的堆栈--调试C程序

完整的调试过程&#xff0c;跟踪堆栈变化&#xff0c;32位下。 注意64位和此不同。 a.c代码&#xff1a; #include <stdio.h> int main() { AFunc(5,6);return 0; } int BFunc(int i,int j) {int m 1;int n 2;m i;n j; return m; }int AFunc(int i,int j) {…

听说过代码洁癖,Bug洁癖怎么解?

来源 | Python编程时光&#xff08;ID: Cool-Python&#xff09;当我们写的一个脚本或程序发生各种不可预知的异常时&#xff0c;如果我们没有进行捕获处理的时候&#xff0c;通常都会致使程序崩溃退出&#xff0c;并且会在终端打印出一堆 密密麻麻 的 traceback 堆栈信息来告诉…

POJO、VO、PO、FormBean区别:

首先讲一下四者的概念 POJO&#xff1a;Pure Old Java Object&#xff0c;符合Java Bean属性规范的简单Java对象&#xff0c;通常也称为VO&#xff08;Value Object&#xff0c;值对象&#xff09;。 VO&#xff1a;就是POJO; PO: Persistent Object&#xff0c;持久化对…

oracle中的sql%rowcount,sql%found、sql%notfound、sql%rowcount和sql%isopen

Oracle 存储过程 删除表记录时删除不存在的记录也是显示删除成功 create or replace procedure delDept(p_deptno in dept.deptno%type) is begindelete from dept where deptnop_deptno;dbms_output.put_line(部门删除成功...);exception when others thendbms_output.put_lin…

linux平台的链接与加载

原文是上下两篇 链接与加载(上) — 静态链接链接与加载(下) — 动态链接 为观看方便&#xff0c;现在合并起来。 一.静态链接 示例程序 我们先看一个简单的示例程序&#xff0c;代码如下&#xff1a; /*main.c*/int u 333;int sum(int, int);int main(int argc, char* argv…

预训练模型ProphetNet:根据未来文本信息进行自然语言生成

作者 | 刘大一恒、齐炜祯、晏宇、宫叶云、段楠、周明来源 | 微软研究院AI头条&#xff08;ID:MSRAsia&#xff09;编者按&#xff1a;微软亚洲研究院提出新的预训练模型 ProphetNet&#xff0c;提出了一种新的自监督学习目标——同时预测多个未来字符&#xff0c;在序列到序列的…

模拟进程管理小结,编码规范的重要性

废话不多说了&#xff0c;省的又有衰人找我麻烦。希望我讨厌的&#xff0c;和讨厌我的少来骚扰我&#xff0c;由衷的感谢它们。 我不回那些骚扰&#xff0c;是因为我见到名字就直接删了&#xff0c;看都懒的看了。也别怪我粗鲁&#xff0c;因为我一向是对什么人说什么话 的&…

JSPServlet路径问题

2019独角兽企业重金招聘Python工程师标准>>> 如果带WebRoot&#xff0c;那么js、css、img都应该放到WebRoot目录下&#xff0c;否则访问会有问题。千万不要放在WEB-INF下&#xff0c;因为WEB-INF下的内容只有服务器转发可以访问到&#xff0c;出于安全考虑。 如果不…

Git学习教程(六)Git日志

第六课 Git 日志 内容提要&#xff1a;浏览项目历史&#xff0c;查询指定提交内容&#xff0c;图形化显示分枝和合并...git log是git中最常用的一个命令&#xff0c;执行之后&#xff0c;会显示该项目的提交历史。如果命令不加任何参数&#xff0c;那么就会显示目前所在分枝上&…

汇编包含C代码

反汇编的时候带上C代码便于观察 比较三元表达式和if else的差异 a1.c #include <stdio.h> int main(void) { int a1;int b2;int c0;a (b>c)?1:0;return 0;} a2.c #include <stdio.h> int main(void) { int a1;int b2;int c0;if(b>c){a1;}else{a0;…

无需3D运动数据训练,最新人体姿势估计方法达到SOTA | CVPR 2020

作者 | Muhammed Kocabas译者 | 刘畅出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;人体的运动对于理解人的行为是非常重要的。尽管目前已经在单图像3D姿势和动作估计方面取得了进展&#xff0c;但由于缺少用于训练的真实的3D运动数据&#xff0c;因此现有的基于视频…

Linux内核跟踪之trace框架分析【转】

转自&#xff1a;http://blog.chinaunix.net/uid-20543183-id-1930846.html------------------------------------------本文系本站原创,欢迎转载!转载请注明出处:http://ericxiao.cublog.cn/------------------------------------------一: 前言本文主要是对trace的框架做详尽…

写给Python开发者:机器学习十大必备技能

作者 | Pratik Bhavsar译者 | 明明如月&#xff0c;编辑 | 夕颜来源 | CSDN&#xff08;ID:CSDNnews&#xff09;有时候&#xff0c;作为一个数据科学家&#xff0c;我们常常忘记了初心。我们首先是一个开发者&#xff0c;然后才是研究人员&#xff0c;最后才可能是数学家。我…