10w数据--数组转树形结构

106 阅读2分钟

总结四种扁平数组转树形结构方法,时间和空间复杂度依次降低

1w条数据耗时对比

1w条数据时,arrToTree4 耗时1毫秒内,可忽略
image.png

10w条数据耗时对比

10w条数据时,arrToTree 已无法正常工作
image.png

1、递归

递归是常见方法,只是随数据量提升开销较大。空间复杂度O(k) 时间复杂度O(n^k)
同样是递归,arrToTree 的效率比 arrToTree1 慢数十倍

function arrToTree(arr, parentId) {
  const tree = []
  arr = JSON.parse(JSON.stringify(arr)) //避免污染原数据
  arr.forEach(item => {
    if (item.pid === parentId) {
      item.children = arrToTree(arr, item.id)
      tree.push(item)
    }
  })
  return tree
}

function arrToTree1(arr, parentId) {
  return arr.filter(item => item.pid === parentId).map(item => 
      ({ ...item, children: arrToTree1(arr, item.id) }))
}
2、循环

利用引用数据类型特点做到数据关联。空间复杂度O(1) 时间复杂度O(n^2)

function arrToTree2(arr, parentId) {
  const tree = []
  arr = JSON.parse(JSON.stringify(arr))//避免污染原数据
  arr.forEach(item => {
    item.children = arr.filter(child => child.pid === item.id)
    if (item.pid === parentId) {
      tree.push(item)
    }
  })
  return tree
}
3、索引

利用对象创建id索引,提高查找效率。 利用引用数据类型特点做到数据关联。
空间复杂度O(1) 时间复杂度O(n)

function arrToTree3(arr) {
  const map = {}
  const tree = []
  arr.forEach(item => {//将数组元素id为key,元素为value,添加到对象中
    map[item.id] = { ...item, children: [] }
  })
  for (const id in map) {
    const item = map[id]
    const pItem = map[item.pid]
    if (pItem) {//找到父节点,将自己添加到父节点children中
      pItem.children.push(item)
    } else {//找不到父节点说明当前item是根节点
      tree.push(item)
    }
  }
  return tree
}
4、索引-(推荐)

只循环一次,效率最高。
利用对象创建id索引,提高查找效率。利用引用数据类型特点做到数据关联。
空间复杂度O(1) 时间复杂度O(n)

function arrToTree4(arr, parentId) {
  const map = {}
  const tree = []
  arr.forEach(item => {
    const id = item.id
    const pid = item.pid
    if (!map[id]) {
      map[id] = { ...item, children: [] }
    } else {
      map[id] = { ...item, ...map[id] }// (1)
    }
    if (map[pid]) {
      map[pid].children.push(map[id])
    } else {//没有父节点就先创建索引,等循环到父节点时再做数据合并 ->(1)
      map[pid] = { children: [map[id]] }
    }
    if (pid === parentId) {
      tree.push(map[id])
    }
  })
  return tree
}
构造数据
function getData(){
    let id = 1
    return Array.from({ length: 100000 }, item => ({ pid: Math.floor(id / 3), id: id++ }))
}
// (3^n)<=100000,最大层数=n+1
测试数据
data = [
  {
    id: 1,
    pid: 0,
    label: '第一层1',
    value: '1.1'
  },
  {
    id: 2,
    pid: 0,
    label: '第一层2',
    value: '1.2'
  },
  {
    id: 3,
    pid: 1,
    label: '第二层1',
    value: '2.1'
  },
  {
    id: 5,
    pid: 4,
    label: '第三层1',
    value: '3.1'
  },
  {
    id: 6,
    pid: 4,
    label: '第三层2',
    value: '3.2'
  },
  {
    id: 4,
    pid: 1,
    label: '第二层2',
    value: '2.2'
  }
]