前端面试中的一个常见题目:如何将一个扁平数组的数据结构,转换为树形结构。
例如,有数组 arr 如下:
const arr = [
{ id: 1, pid: 0 },
{ id: 2, pid: 1 },
{ id: 3, pid: 1 },
{ id: 4, pid: 2 },
{ id: 5, pid: 2 },
{ id: 6, pid: 3 },
];
要求编写一个函数 arr2tree(arr),得到输出结果如下:
{
"id": 0,
"children": [
{
"id": 1,
"children": [
{
"id": 2,
"children": [
{
"id": 4
},
{
"id": 5
}
]
},
{
"id": 3,
"children": [
{
"id": 6
}
]
}
]
}
]
}
解题 1
下面给出一个答案。
function arr2tree(arr) {
// 存储节点及其父节点
const parentHash = {};
// 存储节点及其子节点数组
const childHash = {};
for (let item of arr) {
// 填充父节点哈希表
const { id, pid } = item;
parentHash[id] = pid;
// 填充子节点数组哈希
if (!childHash[pid]) {
childHash[pid] = [];
}
childHash[pid].push(id);
}
// rootId 是树的根节点 id
let rootId = arr[0].id;
while (parentHash[rootId] !== undefined) {
rootId = parentHash[rootId];
}
return hash2Tree(childHash, rootId);
// 通过递归函数把哈希结构变换为树形结构
function hash2Tree(hash, id) {
const result = { id: id };
if (hash[id] && hash[id].length > 0) {
result.children = hash[id].map((cid) => hash2Tree(hash, cid));
}
return result;
}
}
这种写法虽然可以得到正确结果,但使用了两个哈希表存储,加上递归调用,对于内存空间占用太大。
解题 2
如果不介意返回值中出现多余字段(比如 pid),可以采用 前端数组转树arrToTree 评论区秋天一阵风的解法:
function arr2tree(arr) {
const hash = {};
const result = [];
for (let item of arr) {
hash[item.id] = item;
}
for (let item of arr) {
const parent = hash[item.pid];
if (parent) {
if (!parent.children) {
parent.children = [];
}
parent.children.push(item);
} else {
result.push(item);
}
}
return result;
}
内存空间占用更小。