[前端高频]数组转树、数组扁平化、深拷贝、JSON.stringify&JSON.parse等手撕

N叉树

var preorder = function(root) {
    //递归
    let res=[];
    //要将结果加入一个数组中,不要直接递归整个函数,要不每次都会重新重置res,需在写一个内部函数
    var dfs=function(root){
        //递归
        if(!root)return;
        //中
        res.push(root.val);
        //判断是否为null,null.forEach会报错TypeError
        if(root.children){
            //子
            root.children.forEach((child)=>{
                dfs(child);
            })
        }
    }
    dfs(root)
    return res;

 // 迭代
    // 用stack
    let res=[];
    if(!root)return res;
    let stack=[root];
    while(stack.length>0){
            let cur=stack.pop();
            res.push(cur.val);
            if(cur.children){
                //要翻转一下,前序遍历二叉树的话应该是将右节点先压入栈!!!
                cur.children.reverse();
                cur.children.forEach((child)=>{
                    stack.push(child);
                })
            }
    }
    return res;

//层次遍历
var levelOrder = function(root) {
    const res=[];
    if(!root)return res;
    const queue=[root];
    while(queue.length){
        let size=queue.length;
        const levelRes=[];
        while(size--){
            let curNode=queue.shift();
            levelRes.push(curNode.val);
            if(curNode.children){
                curNode.children.forEach(node=>queue.push(node));
            }
        }
        res.push(levelRes);
    }
    return res;
};

数组转N叉树并遍历

要将给定的树形结构数据中的所有节点ID提取到数组中,可以采用递归遍历或广度优先遍历的方式。以下是两种常见实现方法及示例:

树深度遍历(递归和迭代[栈])、广度遍历(队列)


在这里插入图片描述

方法一:递归遍历(深度优先)

递归遍历的核心逻辑是先处理当前节点的ID,再递归处理其子节点,适用于层级较深但数据量适中的场景。

function getAllIds(treeArray) {
    const ids = [];
    function traverse(node) {
        ids.push(node.id);
        if (node.children && node.children.length > 0) {
            node.children.forEach(child => traverse(child));
        }
    }
    treeArray.forEach(root => traverse(root));
    return ids;
}

// 示例输入
const input = [
    {
        id: 1,
        name: 'Root',
        children: [
            { id: 2, name: 'Child1', children: [ { id:4, name:'Grandchild', children:[] } ] },
            { id: 3, name: 'Child2', children: [] }
        ]
    }
];

console.log(getAllIds(input)); // 输出: [1, 2, 4, 3]

关键点:

  1. 递归函数:定义内部函数traverse,依次处理当前节点的ID和子节点。
  2. 深度优先:优先遍历到最深层的子节点(如示例中的id:4),再回溯处理兄弟节点。

方法二:广度优先遍历(非递归)

通过队列(Queue)实现层级遍历,先处理父节点,再按层级依次处理子节点,适合需要按层级顺序输出ID的场景。

function getAllIdsBFS(treeArray) {
    const ids = [];
    const queue = [...treeArray]; // 初始化队列为根节点数组
    while (queue.length > 0) {
        const node = queue.shift(); // 取出队首节点
        ids.push(node.id);
        if (node.children && node.children.length > 0) {
            queue.push(...node.children); // 将子节点加入队列
        }
    }
    return ids;
}

console.log(getAllIdsBFS(input)); // 输出: [1, 2, 3, 4]

关键点:

  1. 队列机制:使用队列按层级顺序遍历节点,根节点先入队,子节点依次入队。
  2. 非递归:避免递归可能导致的内存溢出问题,适合处理大规模树数据。

处理多个树形数组

如果存在多个独立的树形数组(例如多个根节点),只需遍历每个树并合并结果:

const multiTreeInput = [
    // 第一个树
    { id: 1, children: [ { id: 2 } ] },
    // 第二个树
    { id: 5, children: [ { id: 6 } ] }
];

const combinedIds = multiTreeInput.flatMap(tree => getAllIds([tree]));
console.log(combinedIds); // 输出: [1, 2, 5, 6]

适用场景

  1. 权限系统:获取角色树中的所有权限ID。
  2. 组织架构:提取部门层级中的全部成员ID。
  3. 数据统计:分析树状结构的节点分布。

边界条件与优化
• 空值处理:若节点无children字段或children为空数组,需避免遍历错误。

• 去重:若树中存在重复ID,可通过Set去重(如[...new Set(ids)])。

• 性能优化:对超大规模数据,广度优先遍历内存占用更低。

在这里插入图片描述

//3.处理较大数据集
// 示例数据 - 较大的数据集
const largeData = [
    { id: 1, name: "Root", parentId: null },
    { id: 2, name: "Child 1", parentId: 1 },
    { id: 3, name: "Child 2", parentId: 1 },
    { id: 4, name: "Grandchild 1", parentId: 2 },
    { id: 5, name: "Grandchild 2", parentId: 2 },
    { id: 6, name: "Grandchild 3", parentId: 3 },
    { id: 7, name: "Great Grandchild", parentId: 4 }
];

// 构建优化后的树形结构
const optimizedTree = buildTreeWithMap(largeData);

// 输出结果
console.log(JSON.stringify(optimizedTree, null, 2));

//推荐使用!!!
function buildTreeWithMap(data){
    const itemMap=new Map();
    let tree=[];
    // 第一次遍历:将所有项存入Map并初始化children数组
    for (const item of data) {
        itemMap.set(item.id, { ...item, children: [] });
        if(item.parentId!==null){
            if(!itemMap.get(item.parentId)) {
                itemMap.set(item.parentId,{children:[]});
            }
            //push的是itemMap.get(item.id),push进去的是子对象的引用
            itemMap.get(item.parentId).children.push(itemMap.get(item.id));
        }else{
            tree.push(itemMap.get(item.id));
        }
    }
    for(let item of data){
        const node=itemMap.get(item.id);
        if(node.children && node.children.length===0){
            //删除属性
            delete node.children;
        }
    }
    return tree;
}


// //递归形式(推荐)
function buildTree2(flatData,parentId=null) {
    const tree=[];
    for(let item of flatData){
        if(item.parentId===parentId) {
            //递归调用子节点
            const children=buildTree2(flatData,item.id);
            //如果有children则才新增属性
            if(children.length) {
                item.children=children;
            }
            tree.push(item);
        }
    }
    return tree;
}


天狼星网络验证,一个可以注入远程分享弹窗,网络验证的工具你还在用本地注册机吗?你想拥有远程分享弹窗的功能吗?即 刻起就摆脱本地,使用网络注册机! 天狼星网络验证破解,支持一键为软件注入弹窗式网络验证,一键为软件注入全屏式网络验证,支持跳加固注入网络验证, 支持远程引流分享弹窗等等,注入后的软件还有独特的签名验证,防二改,防抓包破解 计算机技术发展到今天,很多优秀的软件已经具备了商业价值,很多软件作者希望自己的软件具有注册功能,但是传统的注册 码解决不了多人使用同一个注册码的问题,机器码(本地验证)的方式又比较容易被破解。这些问题都成为大家制作收费软件 的首要问题刀客源码。本系统就是帮助大家解决这一系列问题。如果你想保护自己的知识产权及将自己的知识产权商业化, 你就一定的试试哦。 完全免费使用用户登录、卡密管理等基本功能,无需担心收费功能影响软件使用! 节省软件成本,不需要购置服务器,登录酸酸云科技后台配置自己的应用程序相关设置,客户端直接调用远程webservices就能 实现网络验证! 缩短开发周期不需要过多的繁琐操作,调用酸酸云科技就可以实现验证机制,无需再书写验证用户登录,用户管理,卡密管理等 繁琐的代码,开发软件只要专注程序代码即可! 安全防破解将重要数据存放在服务器中,只有当客户登陆成功后才会返回相应的重要数据,就算被破解,也有盗版弹窗,也是无 法使用! 支持多程序语言开发同时支持多种程序语言,只要程序能够调用DLL的程序语言都可以调用本插件,例如:按键精灵、易语 言、C#、C++等!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GISer_Jinger

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值