废话少说,直接上代码。
js
const data = [
{id: 1, parentId: null, name: "Parent Node 1"},
{id: 5, parentId: 2, name: "Child Node 2.1"},
{id: 6, parentId: 2, name: "Child Node 2.2"},
{id: 2, parentId: null, name: "Parent Node 2"},
{id: 3, parentId: 1, name: "Child Node 1.1"},
{id: 4, parentId: 1, name: "Child Node 1.2"},
{id: 7, parentId: 3, name: "Grandchild Node 1.1.1"}
];
function listToTree(data) {
const map = {};
data.forEach(item => {
const node = {...item, children: []}
if (!map[item.id]) {
map[item.id] = node.children
}
const itemParentId = item.parentId || 0
if (!map[itemParentId]) {
map[itemParentId] = [node]
} else {
map[itemParentId].push(node)
}
node.children = map[item.id]
})
return map[0] || []
}
时间复杂度:O(n)
空间复杂度:O(n)
接下来说一说为什么说它是复杂度最低,效率最高的方法。
先看看常规 listToTree 实现方法。
1.递归
js
function listToTree(data) {
function loop(key) {
const arr = [];
data.forEach((item) = > {
if (item.parentId === key) {
const newItem = {...item, children: loop(item.id)
}; // 深拷贝并递归
arr.push(newItem);
}
});
return arr;
}
return loop(null); // 从根节点开始 (parentId 为 null)
}
时间复杂度:O(n^2)
- 每次调用
loop
函数时,都会遍历整个data
数组,导致在最坏情况下(树非常深或宽)时间复杂度为 O(n^2),其中 n 是节点的数量。
空间复杂度:O(n)
- 由于递归调用会使用栈来存储每次调用的状态,最坏情况下(树的深度为 n),空间复杂度为 O(n)。此外,返回的树结构也需要 O(n) 的空间。
2. 实现2
js
function listToTree(data) {
// 先生成一个以 id 为键的对象
const obj = {};
data.forEach((item) => {
obj[item.id] = { ...item, children: [] }; // 确保每个节点都有 children 属性
});
const parentList = [];
data.forEach((item) => {
const parent = obj[item.parentId];
if (parent) {
// 当前项有父节点
parent.children.push(obj[item.id]); // 将当前节点添加到其父节点的 children 中
} else {
// 当前项没有父节点 -> 顶层
parentList.push(obj[item.id]); // 添加到顶层节点列表
}
});
return parentList;
}
// 示例数据
const data = [
{ id: 1, parentId: null, name: "Parent Node 1" },
{ id: 5, parentId: 2, name: "Child Node 2.1" },
{ id: 6, parentId: 2, name: "Child Node 2.2" },
{ id: 2, parentId: null, name: "Parent Node 2" },
{ id: 3, parentId: 1, name: "Child Node 1.1" },
{ id: 4, parentId: 1, name: "Child Node 1.2" },
{ id: 7, parentId: 3, name: "Grandchild Node 1.1.1" }
];
// 使用函数
const tree = listToTree(data);
console.log(JSON.stringify(tree, null, 2));
时间复杂度:O(n)
空间复杂度:O(n)
但是显而易见,这个实现有2次遍历。
结语
这个实现方法我之前在掘金上看到的,但是现在已经找不到那篇文章了,也记不起原本是如何实现的。今天自己试着实现了一下,也是糊里糊涂实现的,主要是利用对象的引用特性实现。
这已经是我能想到最简单的实现了,如果有更好的实现,可以发出来看一看