
本文介绍一种时间复杂度更优的非递归方法,通过构建 id 映射表并逐项追溯祖先链,精准提取某根分类(如 id: 1)下所有直接或深层嵌套的子分类对象。
本文介绍一种时间复杂度更优的非递归方法,通过构建 id 映射表并逐项追溯祖先链,精准提取某根分类(如 id: 1)下所有直接或深层嵌套的子分类对象。
在处理树形分类结构时,常见的递归方案虽直观,但易因重复遍历数组(每次 filter 都扫描全量数据)导致性能下降,尤其当分类数量达数千时尤为明显。本文推荐一种单次遍历 + 祖先路径回溯的优化策略,兼顾可读性与执行效率。
核心思路:反向追溯,而非正向展开
不从根节点出发层层查找子节点(易引发深度递归与重复过滤),而是对每个候选分类,沿 parentCategoryId 向上追溯其完整祖先链,一旦发现某级祖先的 id 匹配目标根分类 ID,即判定该分类属于其子树。
实现步骤详解
-
预构建 ID 映射表:使用 Map 实现 O(1) 时间复杂度的分类查找,避免每次 get 都需遍历数组。
Alibabacloud Sdk Client Initialization For Java下载在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
const categoriesById = new Map( categories.map(cat => [cat.id, cat]) );
-
遍历所有分类,逐个验证归属关系:对每个 cat,用 while 循环向上跳转父节点,直到:
- 找到匹配目标 id → 加入结果;
- 遇到 null 或无效父 ID → 终止追溯;
- 父节点不存在(数据异常)→ 安全退出。
-
关键代码实现:
const getAllChildCategories = (rootCategory) => { const targetId = rootCategory.id; const results = []; for (const cat of categories) { // 跳过根自身(通常不包含在子集内) if (cat.id === targetId) continue; let ancestor = cat; while (ancestor && ancestor.parentCategoryId !== null) { ancestor = categoriesById.get(ancestor.parentCategoryId); if (!ancestor) break; // 数据不一致防护 if (ancestor.id === targetId) { results.push(cat); break; } } } return results; };
完整可运行示例
const categories = [
{ id: 1, name: 'beauty', parentCategoryId: null },
{ id: 2, name: 'health', parentCategoryId: null },
{ id: 3, name: 'hair care', parentCategoryId: 1 },
{ id: 4, name: 'hair oil', parentCategoryId: 3 },
{ id: 5, name: 'kumarika hair oil', parentCategoryId: 4 },
{ id: 6, name: 'supplements', parentCategoryId: 2 }
];
const categoriesById = new Map(
categories.map(cat => [cat.id, cat])
);
const getAllChildCategories = (rootCategory) => {
const targetId = rootCategory.id;
const results = [];
for (const cat of categories) {
if (cat.id === targetId) continue; // 排除根节点自身
let current = cat;
while (current && current.parentCategoryId !== null) {
current = categoriesById.get(current.parentCategoryId);
if (!current) break;
if (current.id === targetId) {
results.push(cat);
break;
}
}
}
return results;
};
// 调用示例:获取 beauty(id:1) 的所有子分类
console.log(getAllChildCategories(categories[0]));
// 输出:
// [
// { id: 3, name: 'hair care', parentCategoryId: 1 },
// { id: 4, name: 'hair oil', parentCategoryId: 3 },
// { id: 5, name: 'kumarika hair oil', parentCategoryId: 4 }
// ]
注意事项与最佳实践
- ✅ 性能优势:整体时间复杂度为 O(n × d)(n 为分类总数,d 为最大嵌套深度),远优于递归版的 O(n²) 最坏情况。
- ⚠️ 数据健壮性:代码中加入 !current 判断,防止因 parentCategoryId 指向不存在 ID 导致运行时错误。
- ? 适用场景:特别适合分类数据静态或低频更新、但查询频繁的后台管理/商品类目系统。
- ? 扩展提示:如需支持多根查询(如同时查 id:1 和 id:2 的子集),可将 targetId 改为 Set 并用 has() 替代 === 判断。
该方案摒弃了易出错的递归状态管理,以清晰的数据流和线性逻辑达成高可靠性与高性能,是处理层级分类查询的推荐实践。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










