前端面试:扁平数组转树形结构

2,032 阅读1分钟

前端面试中的一个常见题目:如何将一个扁平数组的数据结构,转换为树形结构。

例如,有数组 arr 如下:

const arr = [
    { id: 1, pid: 0 },
    { id: 2, pid: 1 },
    { id: 3, pid: 1 },
    { id: 4, pid: 2 },
    { id: 5, pid: 2 },
    { id: 6, pid: 3 },
];

要求编写一个函数 arr2tree(arr),得到输出结果如下:

{
    "id": 0,
    "children": [
        {
            "id": 1,
            "children": [
                {
                    "id": 2,
                    "children": [
                        {
                            "id": 4
                        },
                        {
                            "id": 5
                        }
                    ]
                },
                {
                    "id": 3,
                    "children": [
                        {
                            "id": 6
                        }
                    ]
                }
            ]
        }
    ]
}

解题 1

下面给出一个答案。

function arr2tree(arr) {
    // 存储节点及其父节点
    const parentHash = {};
    // 存储节点及其子节点数组
    const childHash = {};

    for (let item of arr) {
        // 填充父节点哈希表
        const { id, pid } = item;
        parentHash[id] = pid;

        // 填充子节点数组哈希
        if (!childHash[pid]) {
            childHash[pid] = [];
        }
        childHash[pid].push(id);
    }

    // rootId 是树的根节点 id
    let rootId = arr[0].id;
    while (parentHash[rootId] !== undefined) {
        rootId = parentHash[rootId];
    }
    
    return hash2Tree(childHash, rootId);
    
    // 通过递归函数把哈希结构变换为树形结构
    function hash2Tree(hash, id) {
        const result = { id: id };
        if (hash[id] && hash[id].length > 0) {
            result.children = hash[id].map((cid) => hash2Tree(hash, cid));
        }
        return result;
    }
}

这种写法虽然可以得到正确结果,但使用了两个哈希表存储,加上递归调用,对于内存空间占用太大。

解题 2

如果不介意返回值中出现多余字段(比如 pid),可以采用 前端数组转树arrToTree 评论区秋天一阵风的解法:

function arr2tree(arr) {
    const hash = {};
    const result = [];

    for (let item of arr) {
        hash[item.id] = item;
    }

    for (let item of arr) {
        const parent = hash[item.pid];

        if (parent) {
            if (!parent.children) {
                parent.children = [];
            }
            parent.children.push(item);
        } else {
            result.push(item);
        }
    }

    return result;
}

内存空间占用更小。