扁平数组转成tree,你还在用递归吗

364 阅读2分钟

场景

给你一个像下面所示的一维数组,然后把他转成tree

const arr = [
    { id: 1, name: '部门1', pid: 0 },
    { id: 2, name: '部门2', pid: 1 },
    { id: 3, name: '部门3', pid: 1 },
    { id: 4, name: '部门4', pid: 3 },
    { id: 5, name: '部门5', pid: 4 },
]

const tree = [
    {
        "id": 1,
        "name": "部门1",
        "pid": 0,
        "children": [
            {
                "id": 2,
                "name": "部门2",
                "pid": 1,
                "children": []
            },
            {
                "id": 3,
                "name": "部门3",
                "pid": 1,
                "children": [
                    {
                        "id": 4,
                        "name": "部门4",
                        "pid": 3,
                        "children": [
                            {
                                "id": 5,
                                "name": "部门5",
                                "pid": 4,
                                "children": []
                            }
                        ]
                    }
                ]
            }
        ]
    }
]

解法一

直接递归
我自己的解法
const arr = [
    { id: 1, name: '部门1', pid: 0 },
    { id: 2, name: '部门2', pid: 1 },
    { id: 3, name: '部门3', pid: 1 },
    { id: 4, name: '部门4', pid: 3 },
    { id: 5, name: '部门5', pid: 4 },
]

const newArr = [...arr];

const arrayToTree = (arr, data) => {
    arr.forEach(item1 => {
        if(item1.children){
            arrayToTree(item1.children, data)
        }
        newArr.forEach(item2 => {
            if(item2.pid === item1.id){
                if(!item1.children){
                    item1.children = [];
                }
                item1.children.push(item2);
            }
        })
    });
}

arrayToTree(arr, newArr)

const tree = arr.filter(item => item.pid === 0);
最佳解法
const arr = [
    { id: 1, name: '部门1', pid: 0 },
    { id: 2, name: '部门2', pid: 1 },
    { id: 3, name: '部门3', pid: 1 },
    { id: 4, name: '部门4', pid: 3 },
    { id: 5, name: '部门5', pid: 4 },
]

const getChildren = (data, result, pid) => {
  for (const item of data) {
    if (item.pid === pid) {
      const newItem = {...item, children: []};
      result.push(newItem);
      getChildren(data, newItem.children, item.id);
    }
  }
}

const arrayToTree = (data, pid) => {
  const result = [];
  getChildren(data, result, pid)
  return result;
}

const tree = arrayToTree(arr, 0);

解法二

利用对象的地址引用
我自己的解法
const arr = [
    {id: 1, name: '部门1', pid: 0, children: []},
    {id: 2, name: '部门2', pid: 1, children: []},
    {id: 3, name: '部门3', pid: 1, children: []},
    {id: 4, name: '部门4', pid: 3, children: []},
    {id: 5, name: '部门5', pid: 4, children: []},
]

const newArr = [...arr];

arr.forEach((item1, index1) => {
    newArr.forEach((item2, index2) => {
        if(item2.pid === item1.id){
            item1.children.push(arr[index2])
        }
    })
})

const tree = arr.filter(item => item.pid === 0)
最佳解法
const arr = [
    { id: 1, name: '部门1', pid: 0 },
    { id: 2, name: '部门2', pid: 1 },
    { id: 3, name: '部门3', pid: 1 },
    { id: 4, name: '部门4', pid: 3 },
    { id: 5, name: '部门5', pid: 4 },
]

const arrayToTree = () => {
    const arrMap = {};
    const result = [];

    for(const item of arr){
        arrMap[item.id] = {...item, children: []}
    }

    for(const item of arr){
        const { id, pid } = item;
        if(pid === 0){
            result.push(arrMap[id]);
        } else{
            if (!arrMap[pid]) {
                arrMap[pid] = {
                  children: [],
                }
              }
            arrMap[pid].children.push(arrMap[id])
        }
    }
    
    return result;
}

const tree = arrayToTree(arr);

比较

递归的时间复杂度是O(2^n),利用对象指针的是时间复杂度是O(2n) 从性能上看,递归消耗的消耗是比较大的

总结

每个人的实现方式都不一样,我的实现方式相比最佳解法性能上差距还是比较大的,我遍历的嵌套比较多,其实像这种利用对象地址引用的实现类似的还有链表,可以看看我的另一篇文章: 链表