
本文介绍如何修正递归树构建函数,使其正确收集所有叶子节点(无子节点的节点)和父节点(至少有一个子节点的节点)的 ID,解决原代码中 leaf_ids 为空、parent_ids 遗漏深层父节点的问题。
本文介绍如何修正递归树构建函数,使其正确收集所有叶子节点(无子节点的节点)和父节点(至少有一个子节点的节点)的 id,解决原代码中 `leaf_ids` 为空、`parent_ids` 遗漏深层父节点的问题。
在构建无限层级树结构时,仅对当前层级做判断是不够的——子树中产生的 leaf_ids 和 parent_ids 必须逐层向上合并,否则顶层调用将无法感知深层节点的类型信息。原代码中,makeTree() 在递归调用后仅提取了子树的 tree,却忽略了其返回的 leaf_ids 和 parent_ids,导致结果丢失。
以下是修复后的完整实现(关键修改已高亮):
function build(array $array): array
{
if (empty($array)) {
return [
'leaf_ids' => [],
'parent_ids' => [],
'tree' => [],
];
}
// 按 parent_id 分组(NULL/0 视为根)
$groupByParent = [];
foreach ($array as $item) {
$parentId = $item['parent_id'] ?? 0;
$groupByParent[$parentId][] = $item;
}
return makeTree($groupByParent, $groupByParent[0] ?? []);
}
function makeTree(array &$parentList, array $items): array
{
$tree = [];
$leafIds = [];
$parentIds = [];
foreach ($items as $item) {
$hasChildren = isset($parentList[$item['id']]) && !empty($parentList[$item['id']]);
if ($hasChildren) {
// ✅ 关键修复:递归获取子树,并合并 leaf_ids & parent_ids
$subResult = makeTree($parentList, $parentList[$item['id']]);
$item['children'] = $subResult['tree'];
$leafIds = array_merge($leafIds, $subResult['leaf_ids']);
$parentIds = array_merge($parentIds, $subResult['parent_ids']);
$parentIds[] = $item['id']; // 当前节点也是父节点
} else {
$leafIds[] = $item['id']; // 无子节点 → 叶子节点
}
$tree[] = $item;
}
return [
'tree' => $tree,
'leaf_ids' => $leafIds,
'parent_ids' => $parentIds,
];
}
? 注意事项:
-
parent_ids应包含所有拥有至少一个直接子节点的节点 ID(如示例中的1,2,5),而非仅顶层根节点; -
leaf_ids是所有终端节点(即没有任何子节点的节点),如3,4,6; - 原逻辑中
if (!isset($item['children']))判断不可靠,因$item['children']可能被显式设为空数组;应以isset($parentList[$item['id']])为准判断是否可能有子节点; - 使用
?? 0和?? []提升健壮性,避免NULL导致的数组键警告。
运行修复后代码,输入示例数据将输出符合预期的结果:
{
"tree": [ /* ... 树结构保持不变 ... */ ],
"leaf_ids": [3, 4, 6],
"parent_ids": [1, 2, 5]
}
该方案时间复杂度为 O(n),空间复杂度为 O(h)(h 为树高),适用于任意深度的嵌套结构,是构建可扩展树形数据并同步提取元信息的标准实践。










