数组—>树结构
数据如下
const flatArr = [
{ id: '01', parentId: 0, name: '节点1' },
{ id: '011', parentId: '01', name: '节点1-1' },
{ id: '0111', parentId: '011', name: '节点1-1-1' },
{ id: '02', parentId: 0, name: '节点2' },
{ id: '022', parentId: '02', name: '节点2-2' },
{ id: '023', parentId: '02', name: '节点2-3' },
{ id: '0222', parentId: '022', name: '节点2-2-2' },
{ id: '03', parentId: 0, name: '节点3' },
1.递归方式
使用reduce方法
/**
* @params {arr: Array 原数组,parentId: 父节点的Id}
* @return {children: array 子数组}
*/
function DeepCreateTreeAsReduce(arr, parentId) {
function Deep(parentId) {
return arr.reduce((pre, cur) => {
if (cur.parentId === parentId) {
cur.children = Deep(cur.id)
pre.push(cur)
}
return pre
}, [])
}
return Deep(parentId)
}
使用 for of
/**
* @param {arr: array 原数组数组, id: number 父节点id}
* @return {children: array 子数组}
*/
function getChildren(arr, id) {
const res = [];
for (const item of arr) {
if (item.pid === id) { // 找到当前id的子元素
// 插入子元素,每个子元素的children通过回调生成
res.push({...item, children: getChildren(arr, item.id)});
}
}
return res;
}
2. 非递归
使用filter
function getData (arr) {
// 利用两层filter实现
let data = arr.filter(item => {
item.children = arr.filter(e => {
return item.id === e.parentId
})
return !item.parentId
})
return data
}
- 递归方式:每次递归寻找当前节点的子节点时都需要重新遍历一遍数组,时间复杂度为 O(nlogn)
- 非递归方式:从根节点往下寻找子节点,通过 Map 保存每个节点及其子节点的信息,子节点保存的是 Map 上的引用,每个节点的子节点都可以在 Map 中通过 id 找到,时间复杂度为 O(n)
通过上面时间复杂度随数据量增大的走势可以看出,当数据越来越大时,递归算法的耗时将远远大于非递归算法。因此,当数据量小时,使用递归算法有一定的优势,但是当数据大到一定的程度时,递归的做法的劣势将越来越明显,使用非递归算法会快很多!
树结构–>数组
1.递归
/**
* @param {obj: object, res: array}
* @return {arr: array}
*/
function fn(obj, res = []) { // 默认初始结果数组为[]
res.push(obj); // 当前元素入栈
// 若元素包含children,则遍历children并递归调用使每一个子元素入栈
if (obj.children && obj.children.length) {
for(const item of obj.children) {
fn(item, res);
}
}
return res;
}
2.非递归
/**
* @param {obj: object}
* @return {arr: array}
*/
function fn(obj) {
const stack = []; // 声明栈,用来存储待处理元素
const res = []; // 接收结果
stack.push(obj); // 将初始元素压入栈
while(stack.length) { // 栈不为空则循环执行
const item = stack[0]; // 取出栈顶元素
res.push(item); // 元素本身压入结果数组
stack.shift(); // 将当前元素弹出栈
// 逻辑处理,如果当前元素包含子元素,则将子元素压入栈
if (item.children && item.children.length) {
stack.push(...item.children);
}
}
return res;
}