算法练习 | 用for的方式来写打平

119 阅读2分钟

前端项目开发中,虽然大多数数据后端都会处理好给我们,但是基于前后端分离的趋势等原因,我们还是经常会遇到和树打交道的场景。对这种结构,我们最先想到的一定是递归处理,但是递归较为耗费性能,有时候也使用while来替代处理。

而这里,我来提供一种不常见的打平树的方法:用for实现打平

for实现的本质其实和用while是相似的,都是操作一个动态的数组,将子项不停的拿出来放到最外层即可。

 

用for实现打平之:深度遍历算法

一 数组打平

核心逻辑就是使用splice将子数组不断的拿出来插入到原来的位置上,所以每循环到一个多层的结构,就会打平掉一层。

示例

const arr = [1, [[2, [3, 4]]], 5, [6]];

方法

let flatten = (arr) => {
    const copy = JSON.parse(JSON.stringify(arr)); // 拷贝一份用来生成结果
    for(var i = 0; i < copy.length; i++) {
        const current = copy[i];
        if (Array.isArray(current)) { // 检测到当前位置存在多层
            copy.splice(i, 1, ...current) // 将里层打平(...current),插入原位置替换原值
            i--; // 替换过之后,当前位置需要再进行一次检查,所以这里退一位
        }
    }
    return copy;
}

结果

console.log(flatten(arr)); // [1, 2, 3, 4, 5, 6]

二 树打平

核心逻辑就是使用splice将children不断的拿出来插入到父级兄弟位置(后一位)上,所以子孙都会排队插到祖宗后面,最终打平。

示例

const tree = [
    {
        name: '小明前端专家',
        id: 1,
        pid: 0,
        children: [
            {
                name: '小花前端程序媛',
                id: 11,
                pid: 1,
                children: [
                    {
                        name: '小华划水运动员',
                        id: 111,
                        pid: 11,
                    },
                    {
                        name: '小李摸鱼运动员',
                        id: 112,
                        pid: 11,
                    }
                ]
            },
            {
                name: '小红摸鱼程序员',
                id: 12,
                pid: 1,
            }
        ]
    },
    {
        name: '小王内卷王',
        id: 2,
        pid: 0,
        children: [
            {
                name: '小林摸鱼王',
                id: 21,
                pid: 2,
            },
            {
                name: '小李后端程序员',
                id: 22,
                pid: 2,
            }
        ]
    }
]

方法

let flattenTree = (tree) => {
    const copy = JSON.parse(JSON.stringify(tree)); // 拷贝一份用来生成结果
    for(let i = 0; i < copy.length; i++) {
        const current = copy[i];
        if (current.children?.length) { // 检测到当前位置存在子集
            copy.splice(i + 1, 0, ...current.children) // 将children打平(...current.children),插入父级后一位
        }
        delete current.children; // children呗复制到了父级后面,此处删除
    }
    return copy;
}

结果

console.log(flattenTree);

// [
//     {
//         "name": "小明前端专家",
//         "id": 1,
//         "pid": 0
//     },
//     {
//         "name": "小花前端程序媛",
//         "id": 11,
//         "pid": 1
//     },
//     {
//         "name": "小华划水运动员",
//         "id": 111,
//         "pid": 11
//     },
//     {
//         "name": "小李摸鱼运动员",
//         "id": 112,
//         "pid": 11
//     },
//     {
//         "name": "小红摸鱼程序员",
//         "id": 12,
//         "pid": 1
//     },
//     {
//         "name": "小王内卷王",
//         "id": 2,
//         "pid": 0
//     },
//     {
//         "name": "小林摸鱼王",
//         "id": 21,
//         "pid": 2
//     },
//     {
//         "name": "小李后端程序员",
//         "id": 22,
//         "pid": 2
//     }
// ]

用for实现打平之:广度遍历算法

广度遍历不会用于数组打平,这里就以树打平为例进行实现:

方法

let flattenTree = (tree) => {
    const copy = JSON.parse(JSON.stringify(tree));
    for(let i = 0; i < copy.length; i++) {
        const current = copy[i];
        if (current.children?.length) {
            copy.push(...current.children) // 将孙子安顺序放到所有爷爷后面,实现先爷爷后孙子,即为广度遍历
        }
        delete current.children;
    }
    return copy;
}

结果

console.log(flattenTree);

// [
//     {
//         "name": "小明前端专家",
//         "id": 1,
//         "pid": 0
//     },
//     {
//         "name": "小王内卷王",
//         "id": 2,
//         "pid": 0
//     },
//     {
//         "name": "小花前端程序媛",
//         "id": 11,
//         "pid": 1
//     },
//     {
//         "name": "小红摸鱼程序员",
//         "id": 12,
//         "pid": 1
//     },
//     {
//         "name": "小林摸鱼王",
//         "id": 21,
//         "pid": 2
//     },
//     {
//         "name": "小李后端程序员",
//         "id": 22,
//         "pid": 2
//     },
//     {
//         "name": "小华划水运动员",
//         "id": 111,
//         "pid": 11
//     },
//     {
//         "name": "小李摸鱼运动员",
//         "id": 112,
//         "pid": 11
//     }
// ]

(注:代码中JSON.parse方式的深拷贝,只是作为测试使用,实际项目请不要参考这种性能不好的方式。)

参考

整理了一系列的JavaScript树操作方法,不用再一遍又一遍的百度了 (参考了此篇文章的数据demo)