当前位置: 首页 > article >正文

45、如何理解和实现递归?数组扁平化里递归有什么缺陷?

目录一、先给面试里的标准定义什么是递归二、递归的核心组成1. 终止条件2. 当前层逻辑3. 递归子问题三、如何写递归一个通用思路例子求 1 到 n 的和拆解四、递归的执行过程怎么理解1. 递进2. 回归五、数组扁平化是什么六、数组扁平化如何用递归实现方案一最经典递归写法执行逻辑示例七、数组扁平化递归的思路怎么讲八、递归实现数组扁平化的几种写法写法1concat 版写法2闭包 外部结果数组写法3reduce 版递归九、数组扁平化中递归的缺陷1. 调用栈过深可能栈溢出原因2. 性能开销较大3. concat 写法会产生很多中间数组4. 可控性较差5. 调试复杂度较高十、如何更精彩地说“递归的缺陷”十一、递归的替代方案迭代 栈用栈实现数组扁平化思路解释优点十二、递归和迭代方案怎么对比十三、还有哪些扁平化方案1. Array.prototype.flat优点注意2. toString / split问题十四、面试怎么回答更精彩版本1基础标准版版本2高分版十五、如果面试官继续追问可以这样答追问1如何判断一个问题适不适合递归追问2递归和循环的本质区别是什么追问3数组扁平化为什么适合递归追问4如何优化递归版扁平化十六、最适合背诵的面试模板十七、一句话收尾总结一、先给面试里的标准定义什么是递归递归就是一个函数在函数内部调用自身把一个大问题不断拆解成规模更小、结构相同的子问题直到满足终止条件。这里面有三个关键词自己调用自己子问题和原问题结构相同必须有终止条件二、递归的核心组成面试时如果你想答得有条理可以说一个递归函数通常由三部分组成终止条件当前层逻辑递归处理下一层子问题这是非常标准的回答框架。1. 终止条件如果没有终止条件就会无限调用最终导致栈溢出Maximum call stack size exceeded例如function fn() { fn() } fn()这就是无限递归。2. 当前层逻辑每一层递归不能只“继续调用”还要做当前层该做的事。比如遍历树、求和、扁平化时每层都要处理本层的数据。3. 递归子问题将问题缩小继续交给同一个函数处理。比如处理左子树处理右子树处理子数组处理下一节点三、如何写递归一个通用思路你可以给面试官一个方法论我写递归一般会按 4 步明确函数的定义找到终止条件写出当前层逻辑找到和原问题相同的子问题继续递归这个回答会显得你不是“背代码”而是真的会设计递归。例子求 1 到 n 的和function sum(n) { if (n 1) return 1 return n sum(n - 1) }拆解函数定义sum(n)表示 1 到 n 的和终止条件n 1当前层逻辑当前层要加上n子问题剩下的是sum(n - 1)四、递归的执行过程怎么理解这个点说清楚面试会很加分。以sum(4)为例sum(4) 4 sum(3) 4 3 sum(2) 4 3 2 sum(1) 4 3 2 1 10递归本质上会经历两个过程1. 递进不断调用自己把问题压入调用栈。2. 回归当到达终止条件后再一层层返回结果。所以递归和调用栈关系非常密切。五、数组扁平化是什么比如有这样一个多维数组const arr [1, [2, [3, 4], 5], 6]扁平化之后变成[1, 2, 3, 4, 5, 6]这个问题非常适合用递归因为它天然符合一个数组中可能还包含子数组子数组的结构和原数组一致所以可以继续用同样的方法处理这就是“原问题和子问题结构相同”非常典型的递归场景。六、数组扁平化如何用递归实现方案一最经典递归写法function flatten(arr) { let result [] for (const item of arr) { if (Array.isArray(item)) { result result.concat(flatten(item)) } else { result.push(item) } } return result }执行逻辑如果当前元素不是数组直接放进结果如果当前元素还是数组递归展开它再把结果拼接进来示例const arr [1, [2, [3, 4], 5], 6] console.log(flatten(arr)) // [1, 2, 3, 4, 5, 6]七、数组扁平化递归的思路怎么讲面试里你可以这样说数组扁平化适合递归因为数组里的元素如果是数组本质上就是一个规模更小但结构相同的子问题。所以我会遍历当前数组如果当前项不是数组就直接放入结果如果当前项是数组就递归调用 flatten 继续处理最终把所有层级的结果合并起来。这个表达就很标准。八、递归实现数组扁平化的几种写法写法1concat版function flatten(arr) { let res [] for (const item of arr) { if (Array.isArray(item)) { res res.concat(flatten(item)) } else { res.push(item) } } return res }写法2闭包 外部结果数组function flatten(arr) { const res [] function dfs(list) { for (const item of list) { if (Array.isArray(item)) { dfs(item) } else { res.push(item) } } } dfs(arr) return res }这种方式通常比频繁concat更好一些因为少了很多中间数组。写法3reduce版递归function flatten(arr) { return arr.reduce((prev, cur) { return prev.concat(Array.isArray(cur) ? flatten(cur) : cur) }, []) }这个写法更“函数式”但面试里建议先讲普通版更清晰。九、数组扁平化中递归的缺陷这个才是这题真正的重点。很多人只会写递归但面试官更想知道你知道递归会带来什么问题吗1. 调用栈过深可能栈溢出这是递归最大的缺陷之一。如果数组嵌套层级特别深比如let arr [1] for (let i 0; i 100000; i) { arr [arr] }再调用递归版flatten(arr)很可能会报错Maximum call stack size exceeded原因递归每调用一次都会压入一个函数栈帧。JS 的调用栈大小是有限的所以嵌套太深就会爆栈。2. 性能开销较大递归有额外的函数调用成本创建调用栈帧参数传递返回值回收如果数据量很大、层级很多递归往往不如迭代方式稳定。3.concat写法会产生很多中间数组比如这个版本res res.concat(flatten(item))每次concat都可能创建一个新数组带来额外内存分配数据复制成本性能下降所以如果扁平化数组很大这种写法性能不够理想。4. 可控性较差递归虽然简洁但在一些场景下不如循环 / 栈结构那样容易控制不好做深度限制不好做中途暂停不好做超大数据分批处理5. 调试复杂度较高递归调用层级一深调试时容易看懵当前在哪一层哪一步返回了为什么结果重复 / 丢失特别是对于递归不熟的人出 bug 不容易定位。十、如何更精彩地说“递归的缺陷”面试时你可以这样说递归实现数组扁平化的优点是代码简洁、思路自然因为嵌套数组本身就具有递归结构。但它也有明显缺点第一嵌套层级过深时可能导致调用栈溢出第二递归有额外的函数调用开销第三如果用concat合并结果会创建很多中间数组性能和内存开销都比较大。所以在数据量大或者嵌套层级不确定的场景下我会更倾向于使用迭代 栈的方式来实现。这段就非常像成熟回答。十一、递归的替代方案迭代 栈如果面试只会递归通常还不够。你最好顺带说一个非递归解法非常加分。用栈实现数组扁平化function flatten(arr) { const stack [...arr] const res [] while (stack.length) { const item stack.pop() if (Array.isArray(item)) { stack.push(...item) } else { res.push(item) } } return res.reverse() }思路解释先把原数组元素放进栈里每次取出一个元素如果它是数组就继续展开压回栈如果不是数组就加入结果因为栈是后进先出最后要reverse()优点不依赖函数递归不容易爆调用栈更适合处理深层嵌套十二、递归和迭代方案怎么对比方案优点缺点递归代码简洁符合问题结构易于表达深层嵌套可能栈溢出函数调用开销大迭代 栈更稳定可控性强不容易爆栈写法相对复杂可读性略低十三、还有哪些扁平化方案如果面试官继续追问你还可以提1.Array.prototype.flatarr.flat(Infinity)例如const arr [1, [2, [3, 4], 5], 6] console.log(arr.flat(Infinity))优点简洁原生支持注意老环境兼容性要考虑面试一般更关注你手写实现能力2.toString / split这个可以提但不建议作为正规答案。arr.toString().split(,)问题所有值都会变成字符串不适合对象、null、undefined、布尔值等复杂情况所以这只是“技巧”不是严肃的通用实现。十四、面试怎么回答更精彩下面给你几个版本。版本1基础标准版递归本质上是函数调用自身把大问题拆解成结构相同的子问题。实现递归时我一般会先确定三件事函数定义、终止条件、当前层逻辑。以数组扁平化为例如果当前元素不是数组就直接放入结果如果当前元素还是数组就递归继续处理它这样就能不断把多维数组展开成一维数组。不过递归也有缺点尤其在数组扁平化场景里如果嵌套层级太深会导致调用栈溢出另外递归有额外函数调用开销如果使用concat还会产生很多中间数组影响性能。所以在层级较深或者数据量较大的场景下我会考虑用迭代 栈来替代递归实现。版本2高分版我理解递归的核心不是“函数自己调自己”这么简单而是把一个问题拆解为若干个规模更小但结构相同的子问题。所以写递归时我通常会先明确四点函数的定义是什么终止条件是什么当前层要做什么哪一部分可以继续递归。在数组扁平化里这个结构非常典型数组中的某一项如果还是数组它本身就是一个更小的扁平化问题所以很适合递归处理。实现上我会遍历当前数组遇到普通值就直接收集遇到子数组就递归展开。但递归在这个场景里也有几个明显缺陷第一嵌套层级深时容易触发调用栈溢出第二递归本身有函数调用开销第三如果用concat合并结果会频繁创建中间数组导致额外的时间和空间消耗。所以如果只是代码表达清晰递归是很自然的方案但如果我考虑稳定性和大数据量场景会更倾向于使用迭代 栈实现避免深递归带来的风险。十五、如果面试官继续追问可以这样答追问1如何判断一个问题适不适合递归我一般会看两个条件第一问题能不能拆成规模更小但类型相同的子问题第二是否存在明确的终止条件。如果这两个条件都满足通常就比较适合递归。追问2递归和循环的本质区别是什么递归是通过函数调用栈隐式地维护状态循环则通常是显式地通过变量或数据结构维护状态。递归表达力强适合树、链表、分治这类天然分层结构循环通常性能和可控性更好。追问3数组扁平化为什么适合递归因为数组中的元素如果还是数组那么它就是原问题的一个子问题结构完全一致这种“自相似结构”非常适合递归。追问4如何优化递归版扁平化你可以答可以从两个方向优化不用concat改成外部结果数组 push减少中间数组创建如果担心层级太深就改成显式栈的迭代实现从根本上规避调用栈问题。例如优化版递归function flatten(arr) { const res [] function dfs(list) { for (const item of list) { if (Array.isArray(item)) { dfs(item) } else { res.push(item) } } } dfs(arr) return res }十六、最适合背诵的面试模板递归的本质是把一个大问题拆成规模更小但结构相同的子问题所以实现递归时我一般会先明确函数定义、终止条件、当前层逻辑以及递归子问题。以数组扁平化为例遍历数组时如果当前项不是数组就直接收集如果当前项还是数组就递归继续展开因为子数组本身就是一个更小的扁平化问题。不过递归在这个场景下也有缺点第一嵌套过深时容易导致调用栈溢出第二递归有额外函数调用开销第三如果实现里使用concat还会频繁产生中间数组影响性能。所以我认为递归适合表达思路、代码也更自然但在大数据量或深层嵌套场景下我会更倾向于用迭代 栈来实现稳定性更好。十七、一句话收尾总结递归适合解决“结构自相似”的问题数组扁平化就是典型例子但递归的代价是栈深限制和额外开销所以面试里最好同时会讲递归实现和非递归优化方案。

相关文章:

45、如何理解和实现递归?数组扁平化里递归有什么缺陷?

目录 一、先给面试里的标准定义 什么是递归? 二、递归的核心组成 1. 终止条件 2. 当前层逻辑 3. 递归子问题 三、如何写递归?一个通用思路 例子:求 1 到 n 的和 拆解: 四、递归的执行过程怎么理解? 1. 递进…...

昇腾ATC工具实战:如何为PP-OCRv4文本检测模型设置动态输入(Batch/分辨率/Shape)

昇腾ATC工具深度实战:PP-OCRv4文本检测模型动态输入配置全解析 当工业级OCR系统遇到尺寸各异的身份证、发票或模糊的街景文字时,固定输入尺寸的模型往往成为性能瓶颈。某物流公司曾因无法处理不同规格的运单图片,导致识别准确率骤降30%。这正…...

Java 高级特性” 体系(反射 + 枚举 + Lambda)

1.反射 1.1 定义 Java的反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法; 不用 new,不用知道类名,也能操作类。 1.2 用途 框架底层核心(S…...

手把手教你用F1C200s驱动正点原子7寸LCD屏:完整配置流程与LVGL测试

从零构建F1C200s嵌入式GUI系统:正点原子7寸屏驱动与LVGL实战指南 在嵌入式开发领域,显示界面的人机交互体验越来越受到重视。F1C200s作为一款性价比极高的国产ARM9芯片,搭配正点原子7寸LCD屏,能够构建出性能稳定、成本可控的嵌入式…...

2026届最火的降重复率方案推荐榜单

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 于学术研究范畴之内,论文AI网站已然成了提升写作效率的关键工具,这种…...

主流Attention Backend技术选型与实战场景剖析

1. Attention Backend技术全景解析 当你用ChatGPT生成一段文字,或者让Stable Diffusion画一幅画时,背后都有一个关键组件在默默工作——Attention Backend。这就像汽车发动机里的涡轮增压器,虽然用户看不见,却直接决定了AI模型的&…...

亲测口碑好的物联网开发生产厂家分享

亲测口碑好的物联网开发生产厂家分享行业痛点分析在当前物联网开发领域,存在着诸多技术挑战。首先,设备兼容性难题突出,不同品牌、型号的物联网设备通信协议和接口各异,导致系统集成困难。数据表明,约 60%的物联网项目…...

革命性智能交互助手:Live2D AI如何重塑用户体验边界

革命性智能交互助手:Live2D AI如何重塑用户体验边界 【免费下载链接】live2d_ai 基于live2d.js实现的动画小人ai,拥有聊天功能,还有图片识别功能,可以嵌入到网页里 项目地址: https://gitcode.com/gh_mirrors/li/live2d_ai …...

**大模型Agent面试全解析:手把手带你拿下高薪Offer,小白也能收藏学!**

大模型Agent面试全解析:手把手带你拿下高薪Offer,小白也能收藏学! 本文分享了作者在阿里大模型Agent应用算法岗的三轮面试经历,涵盖Agent核心技术模块(规划、感知、工具、记忆)、微调、提示工程、算法设计、…...

AH1008:一款宽输入10-55V,输出5V/5A的高效同步整流降压DC-DC转换器

在电源管理芯片领域,宽输入电压范围与大电流输出能力往往是衡量产品实用性的重要指标。本文将介绍一款采用同步整流技术的降压型DC-DC转换器——AH1008,探讨其在10-55V输入转5V/5A应用中的技术特点与设计优势。宽输入电压范围,TEL&#xff1a…...

**一周快速上手:传统研发平台接入Agent开发能力的完整指南(含收藏)**

一周快速上手:传统研发平台接入Agent开发能力的完整指南(含收藏) 本文详细介绍了如何在一周内为传统研发平台接入Agent开发能力,采用Next.jsReact和LangGraph构建Agent状态图,通过系统提示词优化、RAG知识库建设&#…...

keil工程点击build报错FCARM - Output Name not specified, please check ‘Options for Target - Utilities‘

kile工程链接时报错FCARM - Output Name not specified, please check ‘Options for Target - Utilities’ 问题:拷贝了一个keil模板例程,对其中地一些代码文件路径做了调整,并重新添加了代码文件。编译没报错,点击buile链接时报…...

支承套零件加工工艺编程及夹具设计(论文 CAD图纸 开题报告 任务书 加工程序)

支承套作为机械传动系统中的关键零件,其加工精度直接影响设备运行的稳定性。针对该零件的加工工艺编程与夹具设计,需从零件结构特性出发,结合加工设备性能参数,制定科学合理的工艺方案。通过分析支承套的轴向定位孔、径向配合面等…...

旋架式加速度过载模拟实验台结构设计与分析(论文+CAD+SolidWorks+开题报告+任务书+外文翻译……)

旋架式加速度过载模拟实验台是机械工程领域中用于模拟极端加速度环境的关键设备,其核心作用在于为航天器、汽车零部件或高过载装备的可靠性测试提供可控的实验条件。通过旋架结构的旋转运动,实验台能够精确复现不同方向、不同幅值的加速度过载场景&#…...

掌握AI Agent,抢占未来先机:收藏这份小白进阶大模型指南!

掌握AI Agent,抢占未来先机:收藏这份小白进阶大模型指南! AI Agent正引领计算机交互革命,超越文本生成,通过“大脑规划工具调用记忆经验”直接操控应用与设备。本文解析其工作原理、行业竞争格局(OpenAI、…...

【VimRAG 】技术解析:阿里通义实验室多模态记忆图 RAG 框架深度剖析

文章目录VimRAG 技术解析:阿里通义实验室多模态记忆图 RAG 框架深度剖析一、引言二、问题根源:传统 RAG 在多模态场景下的三重困境三、核心架构:三大技术组件3.1 多模态记忆图(MMG)3.2 图调制视觉记忆编码(…...

小白程序员必看:零基础转型大模型应用开发,薪资涨幅超30%!收藏版学习路径分享

小白程序员必看:零基础转型大模型应用开发,薪资涨幅超30%!收藏版学习路径分享 本文分享了我从传统后端开发转型大模型应用开发的完整学习路径,分为入门启蒙、进阶夯实、核心突破、效率提升和思维升级五个阶段。重点介绍了提示词工…...

掌握MCP与Skill:大模型小白/程序员的收藏必备学习指南

掌握MCP与Skill:大模型小白/程序员的收藏必备学习指南 本文深入解析AI Agent中MCP与Skill的核心区别:MCP作为连接层解决"AI能访问什么"(外部数据/工具),Skill作为知识层解决"AI知道怎么做什么"&am…...

保姆级教程:用CBLPRD-330k数据集训练你的第一个车牌识别模型(附ResNet18+CTC实战代码)

从零构建车牌识别模型:CBLPRD-330k数据集实战指南 车牌识别技术作为计算机视觉领域的重要应用,正在智能交通、安防监控等场景中发挥越来越大的作用。对于刚入门的开发者来说,如何利用公开数据集快速搭建一个可用的车牌识别模型,往…...

OneAPI部署实操手册:从零配置到多渠道管理,支持腾讯混元、通义千问、文心一言等全生态

OneAPI部署实操手册:从零配置到多渠道管理,支持腾讯混元、通义千问、文心一言等全生态 你是不是也遇到过这样的烦恼?想用通义千问写代码,用文心一言做PPT,用腾讯混元分析数据,结果每个平台都要单独注册、单…...

从水处理到工控安全:WADI数据集在异常检测中的独特价值与应用场景解析

WADI数据集:工业控制系统异常检测的黄金标准与实践指南 工业控制系统(ICS)的安全防护一直是关键基础设施保护的核心议题。想象一下,一座城市的供水系统突然遭到网络攻击,导致水质异常或供水中断——这不仅会造成经济损失,更直接威…...

掌握Context Graph核心逻辑,小白程序员也能轻松入门大模型并收藏学习!

掌握Context Graph核心逻辑,小白程序员也能轻松入门大模型并收藏学习! Context Graph是当前企业AI领域的热点,掌握其核心逻辑有助于程序员和企业AI从业者快速跟上发展。它通过记录企业决策路径与执行过程,弥补了传统数据平台只关注…...

做不规则多变量时序预测,试试ReIMTS递归多尺度框架,我实验涨点明显!

不规则多变量时间序列的预测任务在医疗、气象等领域至关重要,但其面临着采样间隔不均和数据缺失两大挑战。传统方法难以在稀疏数据中捕捉可靠模式,而现有的大型预训练模型多为规则采样数据设计。 针对这些问题,研究者们提出了创新的解决方案…...

C#怎么操作Chart图表控件 C#如何用WinForms Chart控件绑定数据绘制统计图表【控件】

WinForms Chart控件需手动配置Series、ChartArea及数据源映射,否则图表空白或报错;必须设置XValueMember/YValueMembers(区分大小写)、ChartType,日期轴需格式化或转字符串绑定。WinForms 的 Chart 控件不是“绑定即显…...

普通人用基础C语言从零搭建NES模拟器,背后藏着这些局限

一、普通人觉得遥不可及,他用基础C语言做到了好多人一提到NES模拟器,首先就会觉得那是只有专业大佬才做得来的,不是依靠现成框架去拼接,就是凭借复杂技术去累计,普通人想要从零基础开始上手,根本就是不可能…...

【技术解读】DeWave:当离散编码遇见脑电波,开启无标记EEG到文本翻译新范式

1. DeWave:脑电波翻译技术的革命性突破 想象一下,你正躺在医院的病床上,因为某些原因无法说话,但医生和家属却能实时看到你脑海中想表达的文字——这听起来像是科幻电影中的场景,但DeWave技术正在让这种想象变为现实。…...

TB6612电机驱动避坑指南:STM32平衡小车常见问题与解决方案

TB6612电机驱动避坑指南:STM32平衡小车常见问题与解决方案 平衡小车项目是嵌入式开发者的经典练手项目,而TB6612作为一款性价比极高的电机驱动芯片,在STM32平衡小车中应用广泛。但在实际开发过程中,不少开发者会遇到电机不转、PWM…...

使用Nginx搭建文件服务器的全过程

为什么选择 Nginx 作为文件服务器 1.性能优势 高并发处理 - 轻量级,支持大量并发连接低资源消耗 - 内存占用少,CPU使用率低静态文件服务 - 专门优化过的静态文件传输高稳定性 - 长期运行稳定可靠 2.功能特性 简单的配置 - 配置文件简洁明了HTTP基本认证…...

linux安装mysql8.0全过程

查看服务器架构,下载对应安装包1uname -m2.上传解压包到usr/local解压安装包1tar -xvf mysql-8.0.27-linux-glibc2.12-x86_64.tar.xz3.修改解压后的文件夹为mysql1mv mysql-8.0.27-linux-glibc2.12-x86_64 mysql4.创建mysql用户组和用户并修改权限123groupadd mysql…...

Linux删除文件名包含无效编码字符文件的方法

在Linux中,文件名包含无效编码字符或特殊不可见字符时,可能导致此文件无法通过常规方式选中或删除,可以通过下面方法处理1、确认文件名问题检查终端编码环境1echo $LANG # 默认应为 UTF-8(如 en_US.UTF-8)查看文件名…...