树转数组
这题常见于菜单、权限、组织架构:
- 会不会 DFS 或 BFS
- 是否保留
parentId、level等信息 - 是否会污染原始树节点
- 能否反向实现数组转树
- 对递归爆栈是否有概念
示例
const tree = [
{
id: 1,
name: 'A',
children: [
{ id: 2, name: 'B' },
{
id: 3,
name: 'C',
children: [
{ id: 4, name: 'D' }
]
}
]
}
]期望结果
[
{ id: 1, name: 'A', parentId: null, level: 0 },
{ id: 2, name: 'B', parentId: 1, level: 1 },
{ id: 3, name: 'C', parentId: 1, level: 1 },
{ id: 4, name: 'D', parentId: 3, level: 2 }
]树转数组
DFS 递归实现
核心思路
深度优先遍历树。遍历每个节点时,把当前节点除 children 之外的信息放入结果数组,然后递归处理它的子节点
- 准备结果数组
result - 写一个递归函数,参数包含当前节点列表、父节点 id、层级
- 遍历当前层节点
- 取出
children,把剩余字段和parentId、level放入结果 - 如果有子节点,递归处理子节点
function treeToArray(tree) {
const result = []
function dfs(nodes, parentId = null, level = 0) {
for (const node of nodes) {
const { children = [], ...rest } = node
result.push({
...rest,
parentId,
level
})
if (children.length > 0) {
dfs(children, node.id, level + 1)
}
}
}
dfs(tree)
return result
}提示
如果树很深,递归可能爆栈,可以用队列改成广度优先遍历
BFS 迭代实现
function treeToArray(tree) {
const result = []
const queue = tree.map((node) => ({
node,
parentId: null,
level: 0
}))
while (queue.length > 0) {
const { node, parentId, level } = queue.shift()
const { children = [], ...rest } = node
result.push({
...rest,
parentId,
level
})
children.forEach((child) => {
queue.push({
node: child,
parentId: node.id,
level: level + 1
})
})
}
return result
}数组转树
核心是先用 Map 建立 id -> node,再把每个节点挂到父节点的 children 上
function arrayToTree(list) {
const map = new Map()
const roots = []
list.forEach((item) => {
map.set(item.id, {
...item,
children: []
})
})
list.forEach((item) => {
const node = map.get(item.id)
if (item.parentId == null) {
roots.push(node)
return
}
const parent = map.get(item.parentId)
if (parent) {
parent.children.push(node)
}
})
return roots
}