递归处理树形结构的核心是:对每个节点执行操作后递归处理其子节点,需设终止条件防栈溢出;示例包括遍历打印、带路径过滤和按id查找。

递归处理树形结构数据,核心是:对每个节点执行操作,再对其所有子节点重复相同逻辑。关键在于识别终止条件(比如没有子节点),避免无限调用。
基础递归遍历(深度优先)
适用于统计、查找、修改节点属性等场景。假设有如下树结构:
const tree = {
id: 1,
name: '前端',
children: [
{ id: 2, name: 'JavaScript', children: [] },
{ id: 3, name: 'CSS', children: [
{ id: 4, name: 'Flexbox', children: [] }
]
}
]
};
写一个递归函数,打印所有节点名称:
function traverse(node) {
console.log(node.name); // 处理当前节点
if (Array.isArray(node.children) && node.children.length > 0) {
node.children.forEach(child => traverse(child)); // 递归处理每个子节点
}
}
traverse(tree);
// 输出:前端 → JavaScript → CSS → Flexbox
递归构建新树(如过滤或转换)
不直接修改原数据,而是返回处理后的新树。例如:只保留 name 包含 "Java" 的节点及其祖先路径(即“带路径的过滤”):
function filterTree(node, keyword) {
// 先递归处理所有子节点,得到过滤后的子树
const filteredChildren = (node.children || [])
.map(child => filterTree(child, keyword))
.filter(Boolean); // 去掉返回为 null 的节点
// 如果当前节点匹配,或有有效子节点,则保留
if (node.name.includes(keyword) || filteredChildren.length > 0) {
return {
...node,
children: filteredChildren
};
}
return null; // 不满足条件,舍弃该节点
}
const result = filterTree(tree, 'Java');
// 返回包含 'JavaScript' 节点及其父级 '前端' 的精简树
递归查找指定节点(返回路径或目标)
想找到 id 为 4 的节点,并返回它(或从根到它的路径)。推荐用带返回值的递归,遇到目标立刻返回,避免多余遍历:
function findNodeById(node, targetId) {
if (node.id === targetId) return node;
if (Array.isArray(node.children)) {
for (const child of node.children) {
const found = findNodeById(child, targetId);
if (found) return found; // 找到了就立即返回,不继续循环
}
}
return null;
}
console.log(findNodeById(tree, 4)); // { id: 4, name: 'Flexbox', children: [] }
注意事项与避坑点
递归虽简洁,但实际使用需留心:
- 必须有明确的终止条件,否则栈溢出。常见错误是忘记判断 children 是否存在或是否为数组。
- 避免意外修改原对象,尤其做转换时建议用展开运算符或 structuredClone(兼容性注意)复制节点。
- 超深树慎用纯递归,浏览器调用栈有限(通常 10000 层内较安全)。极深结构可改用栈模拟递归(迭代 + while)。
- this 或上下文丢失问题:在类方法中用递归时,确保回调函数正确绑定 this,或改用箭头函数/显式 call。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











