Skip to content

查找

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(结合层级判断)

实际开发中,可根据树的规模和条件复杂度选择合适的方法,大型树建议加入性能优化(如提前终止、缓存等)。