Skip to content

数组转树 / 树转数组

这类题的核心不是 API,而是“父子关系怎么建”和“层级怎么展开”。

数组转树

常见输入通常长这样:

js
const list = [
  { id: 1, value: 1, parent: null },
  { id: 2, value: 2, parent: 1 },
  { id: 3, value: 3, parent: 2 },
  { id: 4, value: 4, parent: 1 },
]

目标是按 idparent 建出树结构。

一个常见解法:先建映射,再挂子节点

js
function arrToTree(arr) {
  const map = new Map()

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

  const roots = []

  for (const item of arr) {
    const node = map.get(item.id)

    if (item.parent == null) {
      roots.push(node)
      continue
    }

    const parentNode = map.get(item.parent)

    if (parentNode) {
      parentNode.children.push(node)
    }
  }

  return roots
}

这类写法的关键点是:

  • 先通过 Map 快速找到节点
  • 第二轮再建立父子关系

为什么通常要先建 Map

因为如果每次找父节点都去数组里重新扫描,效率和代码复杂度都会变差。

Map 的作用就是把:

  • id -> 节点

这层关系先固定下来。

树转数组

树转数组本质上就是遍历树,再把层级信息重新拍平。

js
function treeToArr(tree, parent = null, result = []) {
  for (const node of tree) {
    const { children = [], ...rest } = node

    result.push({
      ...rest,
      parent,
    })

    if (children.length > 0) {
      treeToArr(children, node.id, result)
    }
  }

  return result
}

这里的关键点是:

  • 当前节点先入结果
  • 子节点继续递归
  • 递归时把当前节点 id 作为下一层 parent

一个容易忽略的边界

数组转树时,经常要先想清楚:

  • 根节点是 null0,还是别的值
  • 如果父节点缺失,该节点怎么处理
  • 是否允许多个根节点

这些都不是算法本身的问题,而是数据约定问题。

使用建议

  • 数组转树优先想到“映射 + 二次挂载”。
  • 树转数组优先想到递归遍历。
  • 写之前先确定字段名和根节点规则,不要默认所有数据都干净。

基于 MIT 许可发布