let tree = [
{
id: '1',
title: '节点1',
children: [
{
id: '1-1',
title: '节点1-1'
}
]
},
{
id: '2',
title: '节点2',
children: [
{
id: '2-1',
title: '节点2-1'
},
{
id: '2-2',
title: '节点2-2'
}
]
},
]
1. Tree structure traversal
1.1 Breadth-first traversal
node 1, node 2, node 1-1, node 2-1, node 2-2, first traverse the large node, then traverse the child node
function scopeTreeForeach(tree, func) {
let node, list = [...tree]
while (node = list.shift()) {
func(node)
node.children && list.push(...node.children)
}
}
1.2 Depth-first traversal
1.2.1. First-order traversal (node 1, node 1-1, node 2, node 2-1, node 2-2, first traverse a node and its children, and then traverse another node and its Child node)
function deepTreeForeach(tree, func) {
tree.forEach(data => {
func(data)
data.children && deepTreeForeach(data.children, func) // 递归遍历子树
})
}
1.2.2 Post-order traversal (node 1-1, node 1, node 2-1, node 2-2, node 2, first traverse a child node and its parent node, and then traverse another child node and its parent node)
function deepTreeLaterForeach(tree, func) {
tree.forEach(data => {
data.children && deepTreeForeach(data.children, func) // 递归遍历子树
func(data)
})
}
2. List to tree structure
const list = [
{
id: 0,
parentId: null,
},
{
id: 1,
parentId: 0,
},
{
id: 2,
parentId: 4
},
{
id: 3,
parentId: 4
},
{
id: 4,
parentId: 0
},
]
1. Double-layer for loop implementation
// 此处是数组对象里的id跟数组里另外一个对象里的parentId进行比较,
// 不同于排序时数组里单个值之间相互比较,
// 无法简单的用length-1这种不用和自己比较的操作
function forLoop(list) {
for (var i = 0; i < list.length; i++) {
// console.log("外部")
for (var j = 0; j < list.length; j++) {
// console.log("内部");
// 自己的id不用跟自己的parentId比较
if (i == j) continue
if (list[i].id == list[j].parentId) {
if (!list[i].children) {
list[i].children = [];
}
list[i].children.push(list[j]);
// 因为只有一个父节点,找到后就跳出循环
break;
}
};
return list
};
}
let value1 = forLoop(list)
console.log("双层for循环遍历:", value1);
2.reduce implementation
function listToTree(list) {
let info = list.reduce((pre, item) => (pre[item.id] = item, item.children = [], pre), {
})
return list.filter(item => {
info[item.parentId] && info[item.parentId].children.push(item)
return !item.parentId
})
}
let value2 = listToTree(list)
console.log("reduce遍历:", value2);
//最后
let treeList = list.filter(item => !item.parentId)
console.log(treeList);
3. Tree structure screening (0,1,2,3,4)
function treeFilter(tree, func) {
// 使用map复制一下节点,避免修改到原树
return tree.map(node => ({
...node })).filter(node => {
node.children = node.children && treeFilter(node.children, func)
return func(node) || (node.children && node.children.length)
})
}
4. Tree structure search
4.1. Find Node
function treeFind(tree, func) {
for (const data of tree) {
//相当于func = node => node.id == '2-1'
if (func(data)) {
return data
}
if (data.children) {
const res = treeFind(data.children, func)
if (res) return res
}
}
return null
}
4.2. Find node path (backtracking method)
function treeFindPath(tree, func, path = []) {
// if (!tree) return []
for (const data of tree) {
// 假设满足条件,直接放到数组里
path.push(data.id)
if (func(data)) {
return path
}
if (data.children) {
const res = treeFindPath(data.children, func, path)
// 只有当数组的长度大于0才返回值
if (res.length) return res
}
// 条件都不满足,则直接删除,对应前面的push
path.pop()
}
return []
}
5 Conclusion
For tree structure operations, recursion is the most basic and the easiest to understand. Recursion itself is the idea of loop, so you can use loop to rewrite recursion.