面试官:你知道几种数组扁平化的方法?

面试官:你知道几种数组扁平化的方法?

数组扁平化相信不少掘友在一些面试中被问到过,这在我们日常编程中也是一个常规操作,它需要我们将一个多维数组转化成一个一维数组。当然,我相信很多掘友都能回答上这个问题,但如果面试官要我们多回答几种,可能有些掘友就答不上来了,所以,借着这篇文章,我们今天就一起来汇总一下几种数组扁平化的方式。

1. 普通递归方法

相信掘友们首先能想到的实现数组扁平化解法是使用递归函数。该方法通过遍历数组中的每个元素,检查它是否还是一个数组。如果是,则对子数组进行同样的扁平化操作,并将结果合并到最终的一维数组中。例如:

let arr = [1,2,[3,4,[5,6]]]function flattenArray(arr) {let result = []for (let i = 0; i < arr.length; i++) {if (Array.isArray(arr[i])) {result = result.concat(flattenArray(arr[i]));} else {result.push(arr[i])}}return result
}console.log(flattenArray(arr));  // [ 1, 2, 3, 4, 5, 6 ]

2. reduce() 方法

利用Array.prototype.reduce()函数的累加器特性,遍历数组并将子数组扁平化后的结果合并到最终的一维数组中。其实也是递归,不过不像上面一样用for循环,和上面代码差别不大,这里主要是利用了reduce方法,代码书写起来更简洁。

let arr = [1,2,[3,4,[5,6,[7,8]]]]function flattenWithReduce(arr) {return arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flattenWithReduce(val) : [val]),[]);
}console.log(flattenWithReduce(arr)) // [1, 2, 3, 4, 5, 6, 7, 8]

优点:可读性强,逻辑清晰,无需额外创建函数闭包。 缺点:对于非常深的嵌套结构,性能可能不如使用flat()方法。

3. Array.prototype.flat()

ES6引入了flat()方法,可以直接对数组进行扁平化操作,可以指定扁平化的层级深度。

let arr = [1,2,[3,4,[5,6,[7,8,[9]]]]]function flattenWithFlat(arr) {return arr.flat(Infinity) // Infinity表示完全扁平化所有层级
}console.log(flattenWithFlat(arr)) // [1,2,3,4,5,6,7,8,9]

优点:简洁高效,原生支持,适合现代浏览器环境。 缺点:旧版浏览器不支持此方法,需要polyfill或其他兼容方案。

4. 扩展运算符与concat

利用扩展运算符(…)和concat()结合,逐步展开嵌套数组。

let arr = [1,2,[3,[4],[5,6,[7,8,[9]]]]]function flattenWithSpread(arr) {while (arr.some(item => Array.isArray(item))) {arr = [].concat(...arr)}return arr
}console.log(flattenWithSpread(arr)); // [1,2,3,4,5,6,7,8,9]

优点:代码相对简洁,易于理解。 缺点:循环执行次数取决于数组嵌套深度,效率相对较低。

5. 广度优先搜索与队列

该方法采用广度优先搜索,也就是我们常说的BFS策略来扁平化嵌套数组。首先创建一个队列并将原数组作为初始节点入队。然后进入循环阶段,只要队列不为空,就从队列头部取出一层元素(即当前层级的所有子数组或值),并检查每个元素是否为数组。如果是数组,则将其元素逐个加入队列后部;如果不是数组,则直接将值加入结果数组中。循环直至队列为空,此时结果数组中包含了所有已展开的一维元素。

let arr = [1,2,[3,[4],[5,6,[7,8,[9]]]]]function flattenWithBFS(arr) {const queue = [arr] // 创建队列,初始化为待处理的数组const result = [] while (queue.length > 0) { const levelItems = queue.shift() // 取出队列首部的一层元素for (const item of levelItems) { // 遍历当前层级的每个元素if (Array.isArray(item)) { queue.push(item); } else {result.push(item) }}}return result 
}console.log(flattenWithBFS(arr)) // [1,2,3,4,5,6,7,8,9]

1、优点:

空间效率较高:通过使用队列结构进行层次遍历,能够保证在有限的循环次数内完成对任何深度嵌套数组的扁平化。
无需递归调用栈:相比于递归方法,这种方法避免了因深层嵌套导致的栈溢出问题。

2、缺点:

时间复杂度:尽管比扩展运算符结合concat()的方法有所改进,但在极端情况下(例如,非常深且宽的树形结构),仍需多次迭代才能完全扁平化数组。
代码相对复杂:相较于简单的扩展运算符与concat()结合的方式,此方法涉及队列操作,理解起来可能需要更多时间。

6. 深度优先搜索与堆栈

该方法利用了堆栈数据结构进行深度优先搜索,也就是DFS。首先将待处理的原数组转换为堆栈,并创建一个空的结果数组用于存储扁平化后的一维元素。在循环中,只要堆栈不为空,就从堆栈顶部取出当前元素。如果该元素是数组,则将其所有元素压入堆栈;如果不是数组,则直接将其添加到结果数组的开头。这个过程会不断深入到嵌套数组的最底层,然后再逐层返回,直至堆栈为空。

let arr = [1,2,[3,[4],[5,6,[7,8,[9]]]]]function flattenWithDFS(arr) {const stack = [...arr] // 将原数组转换为堆栈const result = [] while (stack.length > 0) { // 当堆栈中有待处理元素时const current = stack.pop() // 取出堆栈顶部的元素if (Array.isArray(current)) { stack.push(...current) // 将其所有元素压入堆栈} else {result.unshift(current) // 否则将非数组元素添加到结果数组的开头}}return result 
}console.log(flattenWithDFS(arr));  // [1,2,3,4,5,6,7,8,9]

1、优点:

递归思想简化实现:深度优先搜索天然适用于解决递归问题,此方法避免了显式递归调用,但仍保持了递归处理的思想。
无需额外空间复杂度:相比于广度优先搜索需要队列保存每一层级的数据,深度优先搜索仅需一个堆栈即可,对于内存资源有限的情况更为友好。

2、 缺点:

遍历顺序:深度优先搜索可能导致元素在扁平化后的一维数组中的顺序与原始嵌套数组中的层级顺序不同。
栈溢出风险:当面对极深的嵌套数组时,由于使用堆栈进行递归模拟,可能会遇到JavaScript堆栈大小限制导致的栈溢出错误。

7. lodash库的_.flattenDeep()

lodash库提供了一个现成的方法_.flattenDeep(),可以处理任意深度的嵌套数组。

import _ from 'lodash'let arr = [1,2,[3,[4],[5,6,[7,8,[9]]]]] const flattenedArr = _.flattenDeep(nestedArr)console.log(flattenedArr)

优点:第三方库封装,功能强大且经过优化,适用于各种复杂场景。 缺点:增加项目依赖,不适合仅需简单扁平化且关注性能与体积的应用场景。

以上就是七种常见的数组扁平化的方式了,掌握了这七种数组扁平化的方法,这类问题相信你回答起来也会得心应手。今天就聊到这,至于数组扁平化的应用场景本文并未介绍,当然这也是一个非常重要的知识点,不过本文主要是总结一些方法,对于应用场景感兴趣的掘友们可自行搜索哦。
如果还有其他的数组扁平化方法,欢迎大佬在评论区补充哦。我也会一并更新到文章去。觉得文章有帮助可以给个小赞哦。♥(ˆ◡ˆԅ)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【网站项目】154大学生创新创业平台竞赛管理子系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

uniapp+node.js前后端做帖子模块:分享帖子和分享页面(社区管理平台的小程序)

目录 0前提1.一些准备1.1表帖子表 post 1.2总体思路 2.前端3.后端 &#x1f44d; 点赞&#xff0c;你的认可是我创作的动力&#xff01; ⭐️ 收藏&#xff0c;你的青睐是我努力的方向&#xff01; ✏️ 评论&#xff0c;你的意见是我进步的财富&#xff01; 0前提 温馨提示…

d2-crud-plus 使用小技巧(二)—— 路由跳转查询

需求 项目中要在a.vue界面点击表格中数据&#xff0c;携带参数跳转到b.vue界面&#xff0c;并进行查询。需要在d2-crud-plus框架下实现。 解决方法 使用插槽将要点击的数据添加上点击事件&#xff0c;在点击事件中添加路由跳转&#xff0c;并携带参数。 在目标界面使用对外…

单页面应用的优缺点

单页面应用&#xff08;SPA&#xff0c;Single Page Application&#xff09;是一种特殊的Web应用&#xff0c;其所有页面操作和交互都通过JavaScript、AJAX、HTML5和CSS3等前端技术在浏览器中完成&#xff0c;无需每次请求都重新加载整个页面。这种应用的设计模式在近年来越来…

js中的递归

理解 在函数内部可以调用其他函数,如果这个函数在内部调用了自身,那么这个函数就是递归函数 递归的优点 代码简洁性,递归可以用简介的代码实现复杂的问题逻辑清晰易于理解 递归缺点 调用栈溢出,递归中如何递归层次过深,超过了js引擎的调用栈深度限制,就会导致出现 "最…

GIN与Echo:选择正确Go框架的指南

您是否在Go中构建Web应用&#xff1f;选择正确的框架至关重要&#xff01;GIN和Echo是两个热门选择&#xff0c;每个都有其优势和特点。本指南将详细介绍每个框架的特性、速度、社区热度以及它们各自擅长的项目类型。最后&#xff0c;您将能够为您的下一个Web项目选择完美的框架…

C++11常用知识分享(一)【列表初始化 || 简化声明 || 范围for || 左右值 || 可变参数模板】

目录 一. 列表初始化 1&#xff09;用法 2) initializer_list 小节&#xff1a; 二&#xff0c;简化声明 1) &#xff0c;auto 2) &#xff0c;decltype类 3)&#xff0c;nullptr 三&#xff0c;范围for 四&#xff0c;C11后&#xff0c;STL容器变化 五&#xff0c…

一篇文章教会你如何在IOS真机上完美运行React Native

一篇文章教会你如何在IOS真机上完美运行React Native 项目初始化项目配置可能遇到的问题没有账号也没有Team设备上没有打开开发者模式&#xff0c;也没有信任开发者证书 无线调试 项目初始化 在终端使用**npx react-native init ProjectName**初始化React Native项目。 进入项…

运维随录实战(5)之centos搭建jenkins

一,搭建jenkins准备 下载安装jdk环境 -》版本 jdk11 下载安装maven环境 -》版本 maven 3.8.8 git -》版本 1.8.3.1 yum install git jenkins安装版本:2.414.3 下载地址:https://get.jenkins.io/war-stable/2.414.3/jenkins.war 注:jenkins版本与jdk版本有一定的对应关…

力扣550 游戏玩法分析 IV

目录 题目描述 思路整理 1. 首次登录日期 2. 第二天登录 3. 计算比率 实现思路 完整代码及解释 题目描述 Table: Activity ----------------------- | Column Name | Type | ----------------------- | player_id | int | | device_id | int | | ev…

117.移除链表元素(力扣)

题目描述 代码解决 class Solution { public:ListNode* removeElements(ListNode* head, int val) {//删除头节点while(head!NULL&&head->valval){ListNode*tmphead;headhead->next;delete tmp;}//删除非头节点ListNode*curhead;while(cur!NULL&&cur-&g…

安卓虚拟机ART和Dalvik

目录 一、JVM和Dalvik1.1 基于栈的虚拟机字节码指令执行过程 1.2 基于寄存器的虚拟机 二、ART与Dalvikdex2aotAndroid N的运作方式 三、总结 一、JVM和Dalvik Android应用程序运行在Dalvik/ART虚拟机&#xff0c;并且每一个应用程序对应有一个单独的Dalvik虚拟机实例。 Dalvik…

python入门第十七节常用的高级函数

常用的高级函数 常用的高阶函数filter(function, iterable)map(func, *iterables)reduce(function, iterable[, initial]) 常用的高阶函数 定义&#xff1a;参数或返回值为其他函数的函数 filter(function, iterable) function&#xff1a;函数&#xff08;function 必需能够…

Vue.js 实用技巧:深入理解 Vue.set 方法

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

20 个不同的 Python 函数实例

Python 是一种广泛使用的高级编程语言&#xff0c;其函数是 Python 编程中至关重要的概念之一。函数是一段可以重复使用的代码块&#xff0c;可以接收输入参数并返回输出结果。使用函数能够提高代码的可读性、可维护性和重用性。 基础知识 在 Python 中&#xff0c;函数使用关…

异常值检测-箱线图 头歌代码注释

方法一&#xff1a; import pandas as pd import matplotlib.pyplot as plt from scipy import stats import numpy as npdata pd.read_csv("src/death.csv", index_colUnnamed: 0)data data.dropna(axis1, threshdata.shape[0] * 0.2) data data.dropna(axis0, …

【如何在Docker中,修改已经挂载的卷(Volume)】

曾梦想执剑走天涯&#xff0c;我是程序猿【AK】 提示&#xff1a;添加投票&#xff01;&#xff01;&#xff01; 目录 简述概要知识图谱 简述概要 如何在Docker中&#xff0c;修改已经挂载的卷&#xff08;Volume&#xff09; 知识图谱 在Docker中&#xff0c;修改已经挂载…

C#双向链表实现:在当前节点后插入新数据的方法Insert()

目录 1.定义一个泛型节点类并自动属性 2.定义链表类&#xff0c;并实现Append、Print、MoveFirst、 Insert 3.Main方法 1.定义一个泛型节点类并自动属性 /// <summary> /// 定义泛型节点类 /// </summary> /// <typeparam name"T">泛型运算符&…

本地navicate连接vm虚拟机中的mysql5.7docker容器

一&#xff0c;配置 前提是我已经启动的mysql5.7容器 使用 docker ps -a 查看所有的容器 使用 docker exec -it c4f9 bash 进入mysql命令行&#xff0c;注意这个c4f9是容器唯一id&#xff0c;不用写全连接mysql mysql -uroot -p123456&#xff0c;连接成功后 输入 show datab…

蓝桥杯——123

123 二分等差数列求和前缀和数组 题目分析 连续一段的和我们想到了前缀和&#xff0c;但是这里的l和r的范围为1e12&#xff0c;明显不能用O(n)的时间复杂度去求前缀和。那么我们开始观察序列的特点&#xff0c;可以按照等差数列对序列进行分块。如上图&#xff0c;在求前10个…