查找
JavaScript 中树结构的条件查找是非常常见的操作,根据需求不同可分为「查找单个节点」「查找所有符合条件的节点」「查找符合条件的节点路径」等场景。以下是详细的实现方法和示例:
基础树结构定义
先定义一个通用的树结构(以组织架构为例),后续操作均基于此:
javascript
const orgTree = [
{
id: 1,
name: "技术部",
type: "department",
children: [
{
id: 11,
name: "前端组",
type: "team",
children: [
{ id: 111, name: "张三", type: "person", age: 25 },
{ id: 112, name: "李四", type: "person", age: 30 },
],
},
{
id: 12,
name: "后端组",
type: "team",
children: [{ id: 121, name: "王五", type: "person", age: 28 }],
},
],
},
{
id: 2,
name: "产品部",
type: "department",
children: [{ id: 21, name: "赵六", type: "person", age: 35 }],
},
];1. 查找第一个符合条件的节点
从树中找到第一个满足条件的节点(深度优先),适用于「只需找到一个匹配项」的场景。
实现代码
javascript
/**
* 查找第一个符合条件的节点(深度优先)
* @param {Array} tree - 树结构数组
* @param {Function} condition - 条件函数(返回 boolean)
* @returns {Object|null} 找到的节点或 null
*/
function findFirstNode(tree, condition) {
// 递归遍历函数
function traverse(node) {
// 检查当前节点是否符合条件
if (condition(node)) {
return node;
}
// 递归检查子节点(若存在)
if (node.children && node.children.length > 0) {
for (const child of node.children) {
const result = traverse(child);
// 找到后立即返回,终止遍历
if (result) return result;
}
}
// 未找到
return null;
}
// 遍历根节点
for (const rootNode of tree) {
const result = traverse(rootNode);
if (result) return result;
}
return null;
}使用示例
javascript
// 示例1:查找年龄大于28的第一个人(type: 'person')
const node1 = findFirstNode(orgTree, (node) => {
return node.type === "person" && node.age > 28;
});
console.log(node1); // { id: 112, name: '李四', type: 'person', age: 30 }
// 示例2:查找部门名为「产品部」的节点
const node2 = findFirstNode(orgTree, (node) => {
return node.type === "department" && node.name === "产品部";
});
console.log(node2); // { id: 2, name: '产品部', ... }2. 查找所有符合条件的节点
从树中找到所有满足条件的节点,返回数组(包含所有匹配项)。
实现代码
javascript
/**
* 查找所有符合条件的节点
* @param {Array} tree - 树结构数组
* @param {Function} condition - 条件函数
* @returns {Array} 所有符合条件的节点数组
*/
function findAllNodes(tree, condition) {
const result = [];
// 递归遍历并收集符合条件的节点
function traverse(node) {
if (condition(node)) {
result.push(node); // 符合条件则加入结果数组
}
// 继续遍历子节点(即使当前节点符合条件,也不终止)
if (node.children && node.children.length > 0) {
node.children.forEach((child) => traverse(child));
}
}
// 遍历所有根节点
tree.forEach((rootNode) => traverse(rootNode));
return result;
}使用示例
javascript
// 示例1:查找所有「人」节点(type: 'person')
const persons = findAllNodes(orgTree, (node) => node.type === "person");
console.log(persons.length); // 3(张三、李四、王五、赵六)
console.log(persons.map((p) => p.name)); // ['张三', '李四', '王五', '赵六']
// 示例2:查找所有年龄 >= 28 的人
const adults = findAllNodes(orgTree, (node) => {
return node.type === "person" && node.age >= 28;
});
console.log(adults.map((p) => p.name)); // ['李四', '王五', '赵六']3. 查找节点的完整路径
找到符合条件的节点,并返回从根节点到该节点的完整路径(包含所有祖先节点)。
实现代码
javascript
/**
* 查找符合条件的节点及其完整路径
* @param {Array} tree - 树结构数组
* @param {Function} condition - 条件函数
* @returns {Array|null} 路径数组(从根到目标节点)或 null
*/
function findNodePath(tree, condition) {
// 递归遍历并记录路径
function traverse(node, currentPath) {
// 当前路径加入当前节点
const newPath = [...currentPath, node];
// 若当前节点符合条件,返回完整路径
if (condition(node)) {
return newPath;
}
// 遍历子节点
if (node.children && node.children.length > 0) {
for (const child of node.children) {
const result = traverse(child, newPath);
if (result) return result; // 找到后返回路径
}
}
// 未找到,返回 null
return null;
}
// 遍历根节点
for (const rootNode of tree) {
const path = traverse(rootNode, []);
if (path) return path;
}
return null;
}使用示例
javascript
// 示例:查找「王五」的完整路径
const wangwuPath = findNodePath(orgTree, (node) => {
return node.type === "person" && node.name === "王五";
});
// 打印路径节点名称
console.log(wangwuPath.map((node) => node.name));
// 输出:['技术部', '后端组', '王五']4. 按层级条件查找(如特定层级的节点)
查找位于特定层级的节点(根节点为第 1 层,子节点为第 2 层,以此类推)。
实现代码
javascript
/**
* 查找特定层级的节点
* @param {Array} tree - 树结构数组
* @param {number} targetLevel - 目标层级(根节点为 1)
* @returns {Array} 该层级的所有节点
*/
function findNodesByLevel(tree, targetLevel) {
const result = [];
// 递归遍历,记录当前层级
function traverse(node, currentLevel) {
// 若当前层级等于目标层级,加入结果
if (currentLevel === targetLevel) {
result.push(node);
return; // 无需继续遍历子节点(因层级已超过)
}
// 若未到目标层级,继续遍历子节点(层级 +1)
if (node.children && node.children.length > 0) {
node.children.forEach((child) => {
traverse(child, currentLevel + 1);
});
}
}
// 根节点从层级 1 开始
tree.forEach((rootNode) => traverse(rootNode, 1));
return result;
}使用示例
javascript
// 示例1:查找第 2 层的节点(部门的直接子节点,即「组」)
const level2Nodes = findNodesByLevel(orgTree, 2);
console.log(level2Nodes.map((node) => node.name)); // ['前端组', '后端组', '赵六']
// 示例2:查找第 3 层的节点(组的子节点,即「人」)
const level3Nodes = findNodesByLevel(orgTree, 3);
console.log(level3Nodes.map((node) => node.name)); // ['张三', '李四', '王五']5. 复杂条件组合查找
结合多个条件(如「节点类型 + 属性范围 + 父节点条件」)进行查找。
示例场景
查找「技术部」下所有「年龄在 25~30 岁之间的人」。
实现代码
javascript
// 分步实现:先找到「技术部」,再在其子树中查找符合条件的人
function findTechPersonsInAgeRange(tree, minAge, maxAge) {
// 1. 先找到「技术部」节点
const techDept = findFirstNode(tree, (node) => {
return node.type === "department" && node.name === "技术部";
});
if (!techDept) return []; // 未找到技术部
// 2. 在技术部的子树中查找符合条件的人
return findAllNodes([techDept], (node) => {
return node.type === "person" && node.age >= minAge && node.age <= maxAge;
});
}
// 使用示例:查找技术部中年龄 25~30 岁的人
const techPersons = findTechPersonsInAgeRange(orgTree, 25, 30);
console.log(techPersons.map((p) => p.name)); // ['张三', '李四', '王五']6. 性能优化:带终止条件的查找
对于大型树结构(如万级节点),可在条件函数中加入「提前终止」逻辑,避免不必要的遍历。
示例:查找第一个年龄 > 30 的人,并终止遍历
javascript
function findFirstOldPerson(tree) {
let result = null;
let isFound = false; // 标记是否找到
function traverse(node) {
if (isFound) return; // 已找到,直接返回
if (node.type === "person" && node.age > 30) {
result = node;
isFound = true; // 标记为已找到
return;
}
if (node.children && node.children.length > 0) {
node.children.forEach((child) => traverse(child));
}
}
tree.forEach((rootNode) => traverse(rootNode));
return result;
}
// 查找第一个年龄 >30 的人(赵六,35岁)
console.log(findFirstOldPerson(orgTree).name); // '赵六'总结
树结构的条件查找核心是递归遍历 + 条件判断,根据需求选择不同的查找策略:
- 找单个节点:
findFirstNode(找到后立即终止) - 找所有节点:
findAllNodes(收集所有匹配项) - 找路径:
findNodePath(记录从根到目标的完整路径) - 按层级找:
findNodesByLevel(结合层级判断)
实际开发中,可根据树的规模和条件复杂度选择合适的方法,大型树建议加入性能优化(如提前终止、缓存等)。