编程技巧:什么是JavaScript递归

什么是递归

程序调用自身的编程技巧称为递归(recursion)

递归的基本思想是将一个复杂的问题分解成更小、更易于管理的子问题,这些子问题与原始问题相似,但规模更小。

递归的要素

  • 基本情况(Base Case):
    这是递归终止的条件,也就是说,当问题足够小,可以直接解决而不需要进一步递归时,就会返回一个直接的答案。
  • 递归步骤(Recursive Step):
    在这一步中,问题被分解成更小的子问题,并且递归地解决这些子问题。每个递归调用都向基本情况更进一步。

递归与循环的比较

递归和循环是实现重复操作的两种基本方法。它们在不同的场景下各有优势。

场景

  • 递归适合解决可以分解为逐渐缩小的子问题的问题。
  • 循环通常用于需要重复执行固定次数的操作,或者在满足特定条件之前重复执行。

性能

  • 递归由于每次调用都会占用新的栈空间,可能会导致栈溢出,特别是在深度递归时。此外,递归的执行效率通常低于循环,因为它涉及更多的函数调用开销。
  • 循环通常更高效,因为它避免了函数调用的开销,并且可以更直接地控制循环的次数。

限制

  • 递归需要有明确的终止条件,否则会导致无限递归。
  • 循环也需要有明确的终止条件,否则也可能导致无限循环。

阶乘

递归实现阶乘:

基本情况:
基本情况就是结束条件,这可以有很多,比如:
n = 1 时,返回 1

n = 2 时,返回 2 (1 * 2

n = 3 时,返回 6 (1 * 2 * 3

我们使用最简单的结束条件:
n = 1 时,返回 1

递归步骤:
每次递归调用时,都会将之前的结果乘以当前数字,然后返回结果。

实现:

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

console.log(factorial(5)); // 5 * 4 * 3 * 2 * 1 = 120

通过下图,可以看出这个递归函数的实现原理:

循环实现阶乘:

function factorial(n) {
  let result = 1; 

  for (let i = 2; i <= n; i++) {
    result *= i;
  }

  return result;
}

console.log(factorial(5)); // 输出: 120

斐波那契数列

斐波那契数列是一个特殊的数列,其中每个数都是前两个数的和。

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

下来我们实现一个函数,返回斐波那契数列的第 n 个数。

斐波那契数列的递归实现:

基本情况:

如果 n 为 0 或 1 ,直接返回 n

递归步骤:

每次递归调用都返回前两个数的和。

实现:

function fibonacci(n) {
  if (n === 0) return 0;
  if (n === 1) return 1;

  return fibonacci(n - 1) + fibonacci(n - 2);
}

// 计算第6项斐波那契数
console.log(fibonacci(6)); // 输出: 8

斐波那契数列的循环实现:

function fibonacci(n) {
  // 初始化数组,包含前两项
  let fib = [1, 1];

  for (let i = 2; i < n; i++) {
    // 计算当前项,即前两项的和
    fib[i] = fib[i - 1] + fib[i - 2];
  }

  return fib[n - 1];
}

// 计算第6项斐波那契数
console.log(fibonacci(6)); // 输出: 8

尾递归

我们看这个斐波那契数列的递归实现,他的函数调用次数要比循环次数多很多。我们打印出来看下。

let count = 0;
function fibonacci(n) {
  count++;
  if (n === 0) return 0;
  if (n === 1) return 1;

  return fibonacci(n - 1) + fibonacci(n - 2);
}

// 计算第6项斐波那契数
console.log(fibonacci(6)); // 输出: 8
console.log(`调用 ${count} 次`); // 输出: 调用 25 次

函数执行一次,就会创建一个执行上下文,并且压入执行上下文栈,当函数执行完毕的时候,就会将函数的执行上下文从栈中弹出。我们这个递归函数,创建很多执行上下文压入执行上下文栈。

递归次数过多还会导致栈溢出。

在 chrome 浏览器的 Sources 面板中,我们通过 Call Stack 可以看到执行上下文的函数调用栈的具体情况

下来我们开始使用尾递归优化这个递归函数。

尾递归是指递归函数内部的最后一个动作是函数调用。该调用的返回值,直接返回给函数。
这样就可以避免创建新的栈帧。

我们直接将上一次的结果,作为参数传递给下一次的函数调用。

function fibonacci(n, ac1 = 1, ac2 = 1) {
  if (n == 1 || n == 2) {
    return ac2;
  }

  return fibonacci(n - 1, ac2, ac1 + ac2);
}

// 计算第6项斐波那契数
console.log(fibonacci(6)); // 输出: 8

注意:
上面的 fibonacci 函数虽然已经使用了尾递归优化,但还是可能出现栈溢出。

因为 JavaScript 引擎并不总是能够优化尾递归调用以避免栈溢出。这是因为尾递归优化( Tail Call Optimization , TCO )并不是 JavaScript 语言规范的一部分,也不是所有 JavaScript 引擎都实现了这一优化。

在没有 TCO 的环境中,每次递归调用仍然会占用新的栈空间,fibonacci 函数的每次调用都需要保留其执行上下文(包括参数和局部变量),直到该调用的返回值被使用。随着 n 的增加,递归调用的深度也会增加,这最终可能导致栈溢出。

递归应用的业务场景

数组转树

比如在做权限控制、级联选择组件的时候,后端返回的数据可能是数组,前端需要将其转换为树形结构。这时可以使用递归来解决。

JavaScript 实现扁平数组与树结构的相互转换

递归组件

在 Vue 中,递归组件是一种可以调用自身作为其子组件的组件。递归组件非常适合用来展示嵌套的或者树状的数据结构,例如文件系统、组织结构图、菜单列表等。

在做侧边栏可能有多个层级的时候,可以使用递归组件。

在 vue-element-admin 项目中的 SidebarItem 组件 就是使用了递归组件。

总结

使用递归解决问题,重点是如果把问题分解为更小的相同问题,确定递归终止条件,然后递归调用自身。使用递归要注意栈溢出问题,可以考虑使用尾递归优化。

前端的世界总是在不断变化,作为开发者,我们需要保持好奇心和学习热情,不断探索新的技术,只有这样,我们才能在这个快速发展的时代中立于不败之地。介绍一款程序员都应该知道的软件JNPF快速开发平台平台非常好用,是功能的集大成者,任何信息化系统都可以基于它开发出来。

这是一个基于 Java Boot/.Net Core 构建的简单、跨平台快速开发框架。前后端封装了上千个常用类,方便扩展;集成了代码生成器,支持前后端业务代码生成,实现快速开发,提升工作效率;框架集成了表单、报表、图表、大屏等各种常用的 Demo 方便直接使用;后端框架支持 Vue2、Vue3。如果你有闲暇时间,可以做个知识拓展。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/619730.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C语言 | 数据结构】栈

文章目录 前言1、栈1.1栈的概念和定义1.1.2栈的基本概念&#xff1a; 1.2栈的方法(接口)1.3栈的实现方法1.4栈的性质1.5栈的应用1.6栈的结构 2、栈的实现2.1 顺序栈2.1.1 顺序栈的结构体2.1.2 顺序栈的初始化2.1.3 顺序栈的销毁2.1.4 顺序栈的入栈2.1.5 顺序栈的出栈2.1.5 顺序…

聚观早报 | 比亚迪海狮07 EV上市;苹果将升级Siri

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 5月13日消息 比亚迪海狮07 EV上市 苹果将升级Siri OpenAI开发全新技术 沃尔沃EX30车型将上市 SpaceX计划新建发…

template——模板进阶(C++)

在之前的文章中&#xff0c;介绍了模板初阶&#xff1a;Cpp_桀桀桀桀桀桀的博客-CSDN博客 在本篇中将会对模板进一步的讲解。本篇中的主要内容为&#xff1a;非类型模板参数、函数模板的特化、类模板的特化&#xff08;其中包含全特化和偏特化&#xff09;&#xff0c;最后讲解…

【计算机网络篇】数据链路层(9)使用集线器的共享式以太网

文章目录 &#x1f6f8;使用同轴电缆的共享总线以太网 &#x1f386;使用集线器的共享式以太网&#x1f95a;集线器的特点 &#x1f354;10BASE-T星型以太网 &#x1f6f8;使用同轴电缆的共享总线以太网 若总线上的某个机械连接点接触不良或断开&#xff0c;则整个网络通信就不…

【前端工程化指南】Git常见操作之忽略文件

默认情况下&#xff0c;Git管理代码版本时会对所有文件进行跟踪&#xff0c;但有些时候我们并不希望项目中的一些文件上传到远程仓库或公共仓库中&#xff0c;例如密钥&#xff0c;个人隐私文件等。因此Git提供了两种忽略跟踪文件的方式.gitignore文本文件与git rm命令&#xf…

亿级流量下通用的高并发架构设计

既然是亿级用户应用&#xff0c;那么高并发必然是其架构设计的核心要素。 本文我们将介绍高并发架构设计的一些通用设计方案。 关键词&#xff1a;读/写分离、数据缓存、缓存更新、CQRS、数据分片、异步写 本文节选自电子工业出版社博文视点刚刚出版的《亿级流量系统架构设计…

Java随笔1

1.编程中组件的概念&#xff1a; 在编程中&#xff0c;组件&#xff08;Component&#xff09;通常指的是一种可重用的、模块化的代码单元&#xff0c;它封装了特定的功能或用户界面元素&#xff0c;并提供了与其他代码进行交互的接口。组件可以看作是对数据和方法的简单封装&…

ADS基础操作篇2

上篇文章《ADS基础介绍篇1》,对ADS界面,常用小工具及自带设计模板进行了介绍。ADS使用非常方便,含大量的控件和仿真模板。这篇文章我们主要讲解ADS的基础操作,包含Workspace、原理图、symbol的创建,仿真结果查看及优化。 1. 新建Workspace 添加名称及路径后,点击create…

共享充电宝语音芯片ic方案支持远程4g无线更新语音

一、简介 共享充电宝语音芯片ic方案支持远程4g无线wifi蓝牙更新语音 共享充电宝已经是遍布在大街小巷的好产品&#xff0c;解决了携带充电宝麻烦的痛点 但是很多的共享充电宝在人机交互方便&#xff0c;还做得不够好&#xff0c;比如&#xff1a;借、还设备没有语音提示&…

基于SSM的计算机课程实验管理系统的设计与实现(源码)

| 博主介绍&#xff1a;✌程序员徐师兄、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f44…

AI视频教程下载:用ChatGPT制作 YouTube视频的指南

课程大纲&#xff1a; 面向 YouTuber 的 ChatGPT YouTube关键词研究 YouTube标题 YouTube缩略图 YouTube社区帖子 组织您的 YouTube 视频 本课程将通过两个不同领域的YouTube视频&#xff0c;展示如何使用Chat GPT来创建关键词、标题、缩略图、描述和社区帖子。 关键词研…

【Linux网络】Https【下】{CA认证/证书的签发与认证/安全性/总结}

文章目录 1.引入证书【为方案五铺垫】1.1再谈https1.2SSL/TLS1.3CA机构1.4理解数字签名1.4继续铺垫1.5方案五服务端申请证书回顾一二三回顾方案四方案五过程寻找方案五的漏洞客⼾端对证书进⾏认证 2.查看证书2.1查看浏览器的受信任证书发布机构2.2中间⼈有没有可能篡改该证书2.…

Postman工具介绍与安装

一、Postman介绍 Postman 乃是一款对 HTTP 协议予以支持的接口调试及测试工具&#xff0c;其突出特性在于功能强大&#xff0c;并且使用简便、易用性良好。不管是开发人员开展接口调试工作&#xff0c;还是测试人员进行接口测试任务&#xff0c;Postman 均属于首选工具之一。 接…

会声会影2024中文旗舰免费版(Corel VideoStudio)下载安装包附带会声会影软件注册机

一、软件背景及版本概述 会声会影&#xff08;Corel VideoStudio&#xff09;是由加拿大Corel公司发布的一款视频编辑软件&#xff0c;该软件以其功能丰富、操作简便而广受好评。2024年版本在继承之前版本优点的基础上&#xff0c;进行了诸多创新和改进&#xff0c;为用户提供…

2万字干货:如何从0到1搭建一套会员体系(4)

开始本节前还是一样来个灵魂发问&#xff1a;为什么产品需要用户标签&#xff0c;或者用户标签有什么意义/价值&#xff1f; 某些业务场景下使用会员等级无法满足业务需要。比如新用户激活、老用户福利以及沉默客户唤醒等等。 用户等级划分的逻辑和维度有些局限性&#xff0c;…

java项目之共享汽车管理系统(springboot+mysql+vue)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的共享汽车管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 共享汽车管理系统的主要…

什么是数据恢复软件?数据恢复软件怎么下载使用?

“我一直在寻找一款出色的 PC Android数据恢复软件&#xff0c;我可以下载。有很多&#xff0c;但大多数都需要我付钱。你能推荐一个我可以免费下载的好书吗&#xff1f; 奇客数据恢复安卓版是恢复已删除或丢失的Android数据的最安全工具。免费下载奇客数据恢复安卓版下面尝试所…

一分钟带你了解什么是等保测评

等保测评&#xff0c;即网络安全等级保护测评&#xff0c;是依据国家信息安全等级保护制度规定&#xff0c;对信息系统进行安全技术测评和安全管理测评&#xff0c;以确定系统的安全保护水平是否达到预定的安全等级要求。以下是等保测评的相关知识点总结&#xff1a; 测评概述&…

Google: 在新知识上微调大语言模型是否会鼓励产生幻觉?

摘要 当大型语言模型通过监督式微调进行对齐时,它们可能会遇到在预训练期间没有获得的新事实信息。人们经常推测,这可能会教导模型产生事实上不正确的回应的行为,因为模型被训练成生成没有基于其预先存在的知识的事实。在这项工作中,Google研究了这种暴露在新知识下对微调后模…

PCB的盘中孔

目录 一、什么时候可以在焊盘上打孔&#xff1f; 二、什么时候可以在焊盘上打孔&#xff1f; 绘制PCB时经常会遇到空间不够无法走线&#xff0c;这时我们会放置过孔使信号线穿过电路板一侧到达另一侧进行走线&#xff0c;这样既方便走线&#xff0c;也能够节省板子空间。有时…