主题
数组转树 / 树转数组
这类题的核心不是 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 },
]目标是按 id 和 parent 建出树结构。
一个常见解法:先建映射,再挂子节点
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
一个容易忽略的边界
数组转树时,经常要先想清楚:
- 根节点是
null、0,还是别的值 - 如果父节点缺失,该节点怎么处理
- 是否允许多个根节点
这些都不是算法本身的问题,而是数据约定问题。
使用建议
- 数组转树优先想到“映射 + 二次挂载”。
- 树转数组优先想到递归遍历。
- 写之前先确定字段名和根节点规则,不要默认所有数据都干净。
