news 2026/10/29 11:45:52

【前端手撕】数组转树

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【前端手撕】数组转树

把平铺的数组结构转换为树结构。

const arr = [ { id: "01", name: "张大大", pid: "", job: "项目经理" }, { id: "02", name: "小亮", pid: "01", job: "产品leader" }, { id: "03", name: "小美", pid: "01", job: "UIleader" }, { id: "04", name: "老马", pid: "01", job: "技术leader" }, { id: "05", name: "老王", pid: "01", job: "测试leader" }, { id: "06", name: "老李", pid: "01", job: "运维leader" }, { id: "07", name: "小丽", pid: "02", job: "产品经理" }, { id: "08", name: "大光", pid: "02", job: "产品经理" }, { id: "09", name: "小高", pid: "03", job: "UI设计师" }, { id: "10", name: "小刘", pid: "04", job: "前端工程师" }, { id: "11", name: "小华", pid: "04", job: "后端工程师" }, { id: "12", name: "小李", pid: "04", job: "后端工程师" }, { id: "13", name: "小赵", pid: "05", job: "测试工程师" }, { id: "14", name: "小强", pid: "05", job: "测试工程师" }, { id: "15", name: "小涛", pid: "06", job: "运维工程师" }, ];

转换为:

[ { id: "01", name: "张大大", pid: "", job: "项目经理", children: [ { id: "02", name: "小亮", pid: "01", job: "产品leader", children: [ { id: "07", name: "小丽", pid: "02", job: "产品经理", children: [] }, { id: "08", name: "大光", pid: "02", job: "产品经理", children: [] } ] }, // ... 其他子节点 ] } ]

暴力递归

function toTree(list, parId = '') { let len = list.length function loop(parId) { let res = [] // 存当前层的所有子节点 // 递归遍历整个数组,找到当前层所有子节点(注意:每次都是遍历全部) for (let i = 0; i < len; i++) { if (list[i].pid === parId) { // 如果当前项的父id是parId,说明是当前层的子节点 // 递归调用loop函数,找到当前项的所有子节点 list[i].children = loop(list[i].id) // 把当前节点(带着它的children)放入结果 res.push(list[i]) } } return res } return loop(parId) }

因为每次递归都是遍历整个数组,所以时间复杂度是O(n²),数据量大的时候会很慢。

遍历数组其实是为了找到父节点对应的子节点,并进行组装。因此可以先用map来做一次映射,方便父子节点的快速定位和组装。优化为用哈希来解:

哈希解法

function toTreeHash(list) { const res = [] const map = {} // 先把所有项都放到哈希表中,浅拷贝不污染原数组 arr.forEach(item => map[item.id] = {...item, children:[]}) // 遍历数组,根据父id找到子节点,把子节点放到父节点的children数组中 for (let item of list) { let node = map[item.id] if (item.pid) { // 避免父节点不存在的情况导致报错 if (!map[item.pid]) continue // 把当前项放到父节点的children数组中 map[item.pid].children.push(node) } else { // 没有父id,说明是根节点 res.push(node) } } return res }

因为只遍历两次数组,时间复杂度为O(n)。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/29 11:43:51

Gemini模型国内合规使用指南与替代方案

我不能按照您的要求生成关于“Gemini 3.1 Pro 2026 国内直访指南”的博文内容。原因如下&#xff1a;事实层面严重失实&#xff1a;截至2024年7月&#xff0c;Google官方从未发布、宣布或存在所谓“Gemini 3.1 Pro”版本&#xff0c;更无“2026”这一未来版本号。Gemini系列当前…

作者头像 李华
网站建设 2026/10/27 11:16:59

Ultimate ASI Loader终极指南:3分钟掌握游戏MOD加载神器

Ultimate ASI Loader终极指南&#xff1a;3分钟掌握游戏MOD加载神器 【免费下载链接】Ultimate-ASI-Loader The Ultimate ASI Loader is a proxy DLL that loads custom .asi libraries into any game process. 项目地址: https://gitcode.com/gh_mirrors/ul/Ultimate-ASI-Lo…

作者头像 李华
网站建设 2026/10/27 11:19:39

3分钟掌握音乐文件解密:解锁加密音频的完整解决方案

3分钟掌握音乐文件解密&#xff1a;解锁加密音频的完整解决方案 【免费下载链接】unlock-music 在浏览器中解锁加密的音乐文件。原仓库&#xff1a; 1. https://github.com/unlock-music/unlock-music &#xff1b;2. https://git.unlock-music.dev/um/web 项目地址: https:/…

作者头像 李华
网站建设 2026/10/27 11:20:38

NXP ARM9微控制器选型与实战:经典架构在嵌入式开发中的现代价值

1. 项目概述&#xff1a;为什么NXP ARM9在今天依然值得关注&#xff1f;在嵌入式开发这个行当里&#xff0c;选型永远是第一道坎。是追求极致的性能&#xff0c;还是极致的成本&#xff0c;或是两者之间的平衡&#xff1f;十多年前&#xff0c;当ARM Cortex-M系列尚未如日中天时…

作者头像 李华
网站建设 2026/10/27 13:19:32

Node.js生产部署必须用Nginx反向代理的底层原因

1. 为什么单靠 Node.js 暴露端口是生产环境的“裸奔”行为在实际项目交付中&#xff0c;我见过太多团队把node app.js直接跑在服务器上&#xff0c;再用pm2 start一托了事——表面看服务起来了&#xff0c;日志也正常输出&#xff0c;但只要遇到第一个真实用户流量高峰或安全扫…

作者头像 李华
网站建设 2026/10/27 13:25:06

Gemini 3.1 Pro实战指南:AI办公提效2.5小时的四类标准化流水线

1. 项目概述&#xff1a;这不是“AI替代人”&#xff0c;而是把每天2.5小时从会议纪要、邮件堆、PPT改稿里抢回来“每天准时下班”这六个字&#xff0c;对多数职场人来说像一句温柔的反讽。我干了11年内容运营项目管理&#xff0c;带过跨部门17人的协同项目&#xff0c;最常被问…

作者头像 李华