深度优先 + 子节点数优先:JavaScript 递归排序嵌套树结构

11次阅读

深度优先 + 子节点数优先:JavaScript 递归排序嵌套树结构

本文介绍如何对具有嵌套 children 数组的树形对象数组进行深度感知排序:先按最大嵌套深度降序排列,深度相同时按直接子节点数量降序排列,并递归应用于每一层。

本文介绍如何对具有嵌套 children 数组的树形对象数组进行深度感知排序:先按最大嵌套深度降序排列,深度相同时按直接子节点数量降序排列,并递归应用于每一层。

在处理树形数据(如菜单、组织架构、文件目录)时,常需按“结构复杂度”重新排序——即优先展示嵌套更深、分支更丰富的节点。本教程提供一种 纯 JavaScript 递归方案,无需第三方库,支持任意深度嵌套,并严格满足两大排序规则:

  1. 主序:按最大嵌套深度(depth)降序 —— 深度越大越靠前;
  2. 次序:深度相同时,按当前节点的 children.length 降序 —— 子节点越多越靠前。

✅ 关键洞察:所谓“深度”,指从当前节点向下延伸的 最长路径长度(叶子节点深度为 0,仅含空 children 的节点深度为 1),而非层级索引。

核心实现逻辑

我们分两步完成:

  • 第一步:标注深度 —— 使用 setDepth() 递归计算每个节点的 depth 属性(表示其子树最大深度);
  • 第二步:原地排序 —— 使用 sort() 递归排序:先按 depth 降序,再按 children.length 降序,最后清理临时属性。

以下是完整、可直接运行的代码:

立即学习Java 免费学习笔记(深入)”;

function setDepth(nodes, depth = 0) {if (!Array.isArray(nodes) || nodes.length === 0) return 0;    let maxChildDepth = 0;   for (const node of nodes) {// 递归计算子树深度     const childDepth = setDepth(node.children);     node.depth = childDepth + 1; // 当前节点深度 = 子树最大深度 + 1     maxChildDepth = Math.max(maxChildDepth, node.depth);   }   return maxChildDepth; }  function sortTree(nodes) {if (!Array.isArray(nodes)) return;    // 主排序:depth 降序;次排序:children.length 降序   nodes.sort((a, b) => (b.depth || 0) - (a.depth || 0) || (b.children?.length || 0) - (a.children?.length || 0));    // 递归排序每个子树   for (const node of nodes) {sortTree(node.children);     delete node.depth; // 清理临时 depth 字段,保持数据纯净   } }  // ✅ 使用示例 const tree = [{value: '1',   children: [     {       value: '1.1',       children: [{ value: '1.1.1', children: [] }]     },     {value: '1.2',       children: [{         value: '1.2.1',         children: [           { value: '1.2.1.1', children: [] },           {value: '1.2.1.2', children: [] }         ]       }]     },     {value: '1.3',       children: [{         value: '1.3.1',         children: [{           value: '1.3.1.1',           children: [{ value: '1.3.1.1.1', children: [] }]         }]       }]     }   ] }];  setDepth(tree); sortTree(tree);  console.log(JSON.stringify(tree, null, 2)); // 输出中,'1.3'(深度 4)排第一,'1.2'(深度 3)次之,'1.1'(深度 2)最后 —— 符合预期

注意事项与最佳实践

  • depth 定义一致性:本方案中 depth 表示「该节点向下可达的最大层数」(根节点自身计为第 1 层)。例如:{value:’x’, children:[]} → depth = 1;{value:’y’, children:[{children:[]}]} → depth = 2。
  • 空 children 安全:代码显式检查 node.children?.length,兼容 children: undefined 或 null 场景。
  • 原地排序 & 无副作用:sortTree() 直接修改原数组,且自动删除 depth,避免污染业务数据。
  • 性能提示:时间复杂度为 O(N × D)(N 为总节点数,D 为最大深度),适用于中等规模树(≤ 10k 节点)。超大规模建议结合 Memoization 缓存深度值。
  • 扩展建议:如需升序、或增加第三级排序(如按 value 字典序),只需调整 sort() 回调函数即可。

通过此方法,你将获得一个结构清晰、语义明确、可复用的树排序工具,轻松支撑动态菜单折叠、可视化层级渲染、AI 思维链排序等高级场景。

text=ZqhQzanResources