树形结构转化为扁平化的数组,以代码示例区别和转化方法

698 阅读3分钟

前言

树形结构存储数据是很方便的,里面有什么数据能够一目了然,但是在处理大量数据时,如果使用树形结构,增删改查所需要的时间复杂度会较高,让我们来看个例子

树形结构

以下是一个简单的树形结构数据

const tree = [
    { id:'01',value: 1, children: [
      { id:'01-1',value: 2, children: [
        { id:'01-1-1',value: 3},
        { id:'01-1-2',value: 4}
      ] },
      { id:'01-2',value: 5}
    ]},
    { id:'02',value: 6}
];

如果需要查找,最开始想到的算法就是递归

// 查找节点
const findValue=4  // 需要查找的节点值
const findNode=(tree)=>{
  for(let node of tree){
    if(node.value === findValue){  // 找到节点后返回
      return node
    }
    else if(node.children && findNode(node.children)){ // 如果有 children,就递归查找子树的节点
      return findNode(node.children)
    }
  }
}
const getNode=findNode(tree)
console.log(JSON.stringify(getNode));  // {"id":"01-1-2","value":4}

当 findValue 为根节点时为最好情况,时间复杂度是 O(1),如果 findValue 在树的最深叶子节点上,或者不存在,那么时间复杂度是 O(n),当树的深度较大时,查找所需要的时间将更多,维护树形结构的成本也更大

有什么其他方法可以降低这个复杂度呢,这就需要将树形结构扁平化

扁平化

先让我们来看扁平化后的数组

const flatArray=[
  {id:'01',value:1}, // 没有父节点 就将 parentId 设为 undefined 或不写
  {id:'01-1',value:2,parentId:'01'},
  {id:'01-1-1',value:3,parentId:'01-1'},
  {id:'01-1-2',value:4,parentId:'01-1'},
  {id:'01-2',value:5,parentId:'01'},
  {id:'02',value:6},
]

从上面扁平化后的数组可以看出,我们增加了一个属性 parentId,其值为父节点的 id,通过该属性与父节点保持连接,不会破坏原始的数据结构,这个时候要查找节点就方便多了

const findValue=4
const findNode=(arr)=>{
  for(let item of arr){  // 直接遍历就能找到
    if(item.value===findValue) return item
  }
}
const getNode=findNode(flatArray)
console.log(JSON.stringify(getNode)); // {"id":"01-1-2","value":4,"parentId":"01-1"}

怎么转化

怎么将树形结构转化为扁平化的数组,来看简单的示例

const flat=(tree,parentId,res=[])=>{  // 初始 res 为空数组
  for(let node of tree){
    res.push({
      id:node.id,
      parentId:parentId,
      value:node.value
    })
    if(node.children){  // 有子节点就递归遍历,传入的 parentId 参数为父节点 id, 也就是当前 node 的 id
      flat(node.children,node.id,res)
    }
  }
  return res
}
let flatArray=flat(tree,undefined) // 刚开始传入的节点没有父节点, parentId 可以设为 undefined 或者不写
for(let item of flatArray){
  console.log(JSON.stringify(item));  // 输出每个节点
}

输出的结果为:

image-1.png 这样就能转化成功了

也可以使用 reduce 方法,reduce 作为累加器,可以实现同样的效果

const flat = (tree, parentId) => {
  return tree.reduce((res,{id,value,children}) => { // 传入的参数为 上次积累值 res、当前项解构
    res.push({id,parentId,value})
    if(children){ // 如果有子节点就递归遍历
      res=[...res,...flat(children,id)]  // 利用扩展运算符 展开后合并
    }
    return res;
  }, [])  // 初始值为空数组
}
let flatArray=flat(tree,undefined)
for(let item of flatArray){
  console.log(JSON.stringify(item)); // 结果同上图
}

比较

与树形结构相比,扁平化后的数组:

  • 能够简化数据处理,因为线性结构通常更便于操作
  • 添加或删除数据时,也更加方便
  • 可以提高查询和搜索的性能