
本文介绍一种高效构建无限层级树形数组的方法:通过建立页面映射表与引用关系表,将扁平化父子数据动态组装为嵌套结构,适用于菜单、目录等需递归展示的场景。
本文介绍一种高效构建无限层级树形数组的方法:通过建立页面映射表与引用关系表,将扁平化父子数据动态组装为嵌套结构,适用于菜单、目录等需递归展示的场景。
在实际开发中,我们常遇到将两个扁平数组(顶层节点 + 带 referer 的子节点)合并为一棵多级嵌套树的需求。关键挑战在于:子节点的父级可能并非原始顶层节点,而是另一子节点(如 '666.ru' 的 referer 是 '66.ru',而 '66.ru' 本身又是 '6.ru' 的子节点),因此必须支持动态、递归式挂载,而非简单的一层映射。
核心思路是分三步构建中间结构:
-
建立页面索引映射(
$childPageMap):以每个子节点的'page'为键,预置空'childs'数组,便于后续快速查找和挂载; -
逐层向上归并(关键步骤):遍历映射表,若当前节点的
'referer'在映射表中存在,则将其作为子项挂入对应父节点的'childs',并从顶层候选集中移除——这一步实现了“子变父”的链式提升; -
按引用关系聚合子树(
$childRefererMap):将最终未被提升的子节点(即真正属于顶层父级的直接子项)按'referer'分组,再批量注入$array1对应节点的'childs'中。
以下是完整可运行的实现代码(已优化逻辑与健壮性):
<?php $array1 = [
['page'=>'1.ru', 'title'=>'—', 'childs'=>[]],
['page'=>'3.ru', 'title'=>'—', 'childs'=>[]],
['page'=>'6.ru', 'title'=>'—', 'childs'=>[]]
];
$array2 = [
['page'=>'666.ru', 'title'=>'+', 'referer'=>'66.ru'],
['page'=>'33.ru' , 'title'=>'+', 'referer'=>'3.ru'],
['page'=>'66.ru' , 'title'=>'+', 'referer'=>'6.ru']
];
// 步骤1:构建 page → 节点映射,统一初始化 childs
$childPageMap = [];
foreach ($array2 as $item) {
$item['childs'] = [];
$childPageMap[$item['page']] = $item;
}
// 步骤2:递归归并 — 将子节点挂入其 referer 对应的父节点,并移除已挂载项
// 注意:需多次扫描直至无变化,或使用 while 循环确保深度嵌套(本例中 foreach 一次足够,但生产环境建议 while)
$changed = true;
while ($changed) {
$changed = false;
foreach ($childPageMap as $page => $node) {
$parentPage = $node['referer'];
if (isset($childPageMap[$parentPage])) {
// 挂载:将当前节点追加到父节点的 childs 数组中(注意是 [] 追加,非覆盖)
$childPageMap[$parentPage]['childs'][] = $node;
unset($childPageMap[$page]);
$changed = true;
}
}
}
// 步骤3:按 referer 归集剩余子节点(即直接隶属于 $array1 中某 page 的子项)
$childRefererMap = [];
foreach ($childPageMap as $node) {
$referer = $node['referer'];
if (!isset($childRefererMap[$referer])) {
$childRefererMap[$referer] = [];
}
$childRefererMap[$referer][] = $node;
}
// 步骤4:注入顶层数组
foreach ($array1 as &$node) {
$page = $node['page'];
if (isset($childRefererMap[$page])) {
$node['childs'] = array_merge($node['childs'], $childRefererMap[$page]);
}
}
unset($node); // 解除引用
// 输出结果
print_r($array1);
?>
✅ 注意事项:
- 原始方案中
$childPageMap[$currParent]['childs'] = $currRow存在错误(会覆盖而非追加),正确做法是[]=追加; - 若嵌套层级极深或数据量大,建议用
while循环替代单次foreach,确保所有中间父节点都被充分提升; -
referer值必须严格匹配page字段,建议提前校验是否存在循环引用(如 A→B→A),否则将导致无限循环; - 如需支持更复杂场景(如多字段标识、动态字段名),可封装为通用函数并接受配置参数。
该方法时间复杂度接近 O(n),空间可控,是构建动态树结构的经典轻量级解决方案。










