You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用JavaScript实现简单树形结构中所有可行路径的查找及最长路径求解

解决节点结构的所有可行路径与最长路径问题(JavaScript实现)

我完全理解你的困惑——递归处理这类树形/图状路径问题时,很容易绕不清调用逻辑。不过别担心,我们可以一步步拆解这个问题,从构建数据结构到递归遍历,再到最后计算最长路径,整个流程其实很清晰。

核心思路拆解

你的问题本质是遍历从根节点(示例中是index=1)到所有叶子节点(没有子节点的节点)的所有路径,然后从中找出最长的那条(或其长度)。我们可以分三步来实现:

1. 预处理节点数据,提升查找效率

给定的节点数组是线性的,每次查找某个节点的子节点都要遍历数组,效率太低。我们可以把它转换成Map结构,用节点的index作为键,直接映射到对应的子节点列表,这样查找子节点的时间复杂度就是O(1)。

2. 递归遍历所有路径

递归的核心逻辑很简单:

  • 终止条件:当当前节点没有子节点时,这条路径就走到头了,把它保存起来。
  • 递归过程:对于当前节点的每个子节点,把它加入当前路径的副本(避免引用共享导致的路径混乱),然后继续递归处理这个子节点。

3. 计算最长路径

收集完所有路径后,我们可以通过map获取每个路径的长度,再用Math.max找出最大值;如果需要最长路径本身,就过滤出所有长度等于最大值的路径。

完整JavaScript实现代码

// 你的原始节点数据
const nodes = [
  {index: 1, children: [2]},
  {index: 2, children: [3, 4]},
  {index: 3, children: [4]},
  {index: 4, children: []}
];

// 步骤1:构建节点映射表,快速查找子节点
const nodeMap = new Map();
nodes.forEach(node => {
  nodeMap.set(node.index, node.children);
});

// 存储所有可行路径
const allPaths = [];

// 步骤2:递归函数——遍历从当前节点出发的所有路径
function traversePaths(currentIndex, currentPath) {
  const children = nodeMap.get(currentIndex);
  
  // 终止条件:当前节点没有子节点,保存路径(存副本!)
  if (children.length === 0) {
    allPaths.push([...currentPath]);
    return;
  }
  
  // 遍历每个子节点,继续递归
  children.forEach(childIndex => {
    // 创建新的路径副本,避免修改原路径
    traversePaths(childIndex, [...currentPath, childIndex]);
  });
}

// 从根节点(index=1)开始遍历
traversePaths(1, [1]);

// 步骤3:处理结果
console.log("所有可行路径:", allPaths); // 输出 [[1,2,3,4], [1,2,4]]
const maxPathLength = Math.max(...allPaths.map(path => path.length));
console.log("最长路径长度:", maxPathLength); // 输出 4
const longestPaths = allPaths.filter(path => path.length === maxPathLength);
console.log("最长路径:", longestPaths); // 输出 [[1,2,3,4]]

额外扩展:处理所有可能的起点

如果你的场景中存在孤立节点(比如某个节点的index没有出现在任何其他节点的children里),想要遍历所有节点作为起点的路径,只需要修改调用递归的部分:

// 遍历所有节点作为起点
nodes.forEach(node => {
  traversePaths(node.index, [node.index]);
});

这样就能收集到所有可能的路径,包括孤立节点自身的路径(比如如果有节点5,children为空,路径就是[5])。

关键细节说明

  • 为什么要存路径副本?:数组是引用类型,如果直接传递原数组,递归过程中修改数组会影响其他分支的路径。用扩展运算符[...currentPath]创建新数组,就能保证每个递归分支的路径都是独立的。
  • 递归的终止条件:必须是当前节点没有子节点时才保存路径,否则会漏掉后续的子节点路径。
  • 节点映射表的作用:当节点数量很多时,用Map比每次遍历原数组查找子节点效率高得多,能显著提升性能。

内容的提问来源于stack exchange,提问作者iNSIDE the mirror

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 08:17:48