在处理家族关系相关的业务场景时,我们经常会遇到无限层级的家族树结构,每个家族节点下可能包含多个子节点,需要统计每个节点对应的全部成员数量,包括节点自身和其所有后代成员。PHP的递归特性可以很好地适配这种层级不确定的统计需求。

家族树数据结构设计
首先我们需要定义家族树的存储格式,通常使用数组来表示每个家族节点,每个节点包含自身的成员信息和子节点列表。常见的结构如下:
<?php
// 家族树示例数据,每个节点包含id、name和children子节点数组
$familyTree = [
[
'id' => 1,
'name' => '祖父',
'children' => [
[
'id' => 2,
'name' => '父亲',
'children' => [
['id' => 4, 'name' => '儿子', 'children' => []],
['id' => 5, 'name' => '女儿', 'children' => []]
]
],
[
'id' => 3,
'name' => '叔叔',
'children' => [
['id' => 6, 'name' => '堂兄弟', 'children' => []]
]
]
]
]
];
?>
递归计数函数实现
递归函数的核心逻辑是:对于每个传入的家族节点,首先计数自身为1,然后遍历该节点的所有子节点,递归调用计数函数得到每个子节点的成员数,累加后返回总数量。实现代码如下:
<?php
/**
* 递归统计家族节点下的总成员数
* @param array $node 家族节点数组
* @return int 该节点下的总成员数(包含自身)
*/
function countFamilyMembers(array $node): int {
// 自身算1个成员
$count = 1;
// 判断是否存在子节点
if (!empty($node['children'])) {
// 遍历所有子节点,递归统计每个子节点的成员数并累加
foreach ($node['children'] as $child) {
$count += countFamilyMembers($child);
}
}
return $count;
}
?>
调用示例与结果验证
我们可以通过调用上述函数来验证不同节点的统计结果:
<?php // 统计根节点(祖父)的总成员数 $rootMemberCount = countFamilyMembers($familyTree[0]); echo "祖父节点下的总成员数:" . $rootMemberCount . PHP_EOL; // 输出6 // 统计父亲节点的总成员数 $fatherMemberCount = countFamilyMembers($familyTree[0]['children'][0]); echo "父亲节点下的总成员数:" . $fatherMemberCount . PHP_EOL; // 输出3 // 统计叔叔节点的总成员数 $uncleMemberCount = countFamilyMembers($familyTree[0]['children'][1]); echo "叔叔节点下的总成员数:" . $uncleMemberCount . PHP_EOL; // 输出2 ?>
注意事项
- 如果家族树的层级非常深,递归可能会导致栈溢出,这种情况下可以考虑改用迭代方式实现,或者使用尾递归优化(需要PHP开启对应配置)。
- 实际业务中如果家族树数据是从数据库读取的,建议提前对数据进行层级处理,避免重复查询数据库导致的性能问题。
- 如果节点数据中包含额外的成员数量字段,也可以在递归时直接累加,减少重复计算的开销。
扩展:批量统计所有节点成员数
如果需要一次性统计家族树中所有节点的成员数,可以结合递归遍历所有节点并调用计数函数:
<?php
/**
* 批量统计家族树所有节点的成员数
* @param array $nodes 家族节点数组
* @return array 节点id到成员数的映射
*/
function batchCountFamilyMembers(array $nodes): array {
$result = [];
foreach ($nodes as $node) {
$result[$node['id']] = countFamilyMembers($node);
// 递归处理子节点
if (!empty($node['children'])) {
$result += batchCountFamilyMembers($node['children']);
}
}
return $result;
}
$allCounts = batchCountFamilyMembers($familyTree);
print_r($allCounts);
// 输出:Array ( [1] => 6 [2] => 3 [4] => 1 [5] => 1 [3] => 2 [6] => 1 )
?>