1. 扁平化数组与树形结构的概念解析
在数据处理领域,扁平化数组和树形结构是两种常见的数据组织形式。扁平化数组通常指一个线性列表,其中每个元素都是独立且平级的,没有明显的层级关系。而树形结构则是一种非线性的数据结构,元素之间存在父子层级关系。
典型扁平化数组示例:
javascript复制const flatArray = [
{ id: 1, name: '节点1', parentId: null },
{ id: 2, name: '节点2', parentId: 1 },
{ id: 3, name: '节点3', parentId: 1 },
{ id: 4, name: '节点4', parentId: 2 }
]
对应树形结构:
javascript复制{
id: 1,
name: '节点1',
children: [
{
id: 2,
name: '节点2',
children: [
{ id: 4, name: '节点4', children: [] }
]
},
{ id: 3, name: '节点3', children: [] }
]
}
这种转换在前端开发中尤为常见,比如:
- 渲染级联选择器
- 构建导航菜单
- 处理组织架构数据
- 展示文件目录结构
关键点:转换的核心是通过parentId建立节点间的关联关系,然后递归构建嵌套的children数组。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心算法实现与优化
2.1 基础递归实现
最直观的方法是使用递归算法,时间复杂度为O(n^2):
javascript复制function buildTree(items, parentId = null) {
const result = []
for (const item of items) {
if (item.parentId === parentId) {
const children = buildTree(items, item.id)
if (children.length) {
item.children = children
}
result.push(item)
}
}
return result
}
性能瓶颈:
- 每次递归都要遍历整个数组
- 大量重复的if条件判断
- 不适合处理大规模数据(超过1000节点)
2.2 使用哈希表优化
通过引入Map存储节点引用,可将时间复杂度优化到O(n):
javascript复制function buildTreeOptimized(items) {
const map = new Map()
const roots = []
// 第一遍遍历:建立哈希映射
for (const item of items) {
map.set(item.id, { ...item, children: [] })
}
// 第二遍遍历:构建树结构
for (const item of items) {
const node = map.get(item.id)
if (item.parentId === null) {
roots.push(node)
} else {
const parent = map.get(item.parentId)
if (parent) {
parent.children.push(node)
}
}
}
return roots
}
性能对比(1000节点测试数据):
| 方法 | 执行时间(ms) | 内存占用(MB) |
|---|---|---|
| 基础递归 | 45.2 | 12.4 |
| 哈希表优化 | 3.8 | 8.7 |
2.3 处理循环引用问题
实际业务中常遇到循环引用的情况,需要添加防护机制:
javascript复制function buildTreeSafe(items) {
const map = new Map()
const visited = new Set()
const roots = []
// 建立映射
items.forEach(item => {
map.set(item.id, { ...item, children: [] })
})
// 构建树并检测循环
function buildNode(id, path = new Set()) {
if (path.has(id)) {
console.warn(`检测到循环引用: ${[...path, id].join(' -> ')}`)
return nu
