CCF-GESP计算机学会等级考试2026年6月六级C++T2 满二叉树
P17013 [GESP202606 六级] 满二叉树
题目描述
给定一棵包含 n n n 个结点的有根二叉树,结点依次以 1 , 2 , … , n 1, 2, \dots, n 1,2,…,n 编号,根结点编号为 1 1 1。
对于结点 i i i,其左儿子的编号记为 l i l_i li,右儿子编号记为 r i r_i ri。特别地,如果左儿子不存在则 l i = 0 l_i = 0 li=0,如果右儿子不存在则 r i = 0 r_i = 0 ri=0。
树中每个结点都对应一棵以其为根的子树。请你求出给定有根树的所有 n n n 棵子树中,有多少棵子树是满二叉树。
满二叉树是指所有叶子深度均相同,且除叶子外均有两个儿子的二叉树,例如以下三棵二叉树均是满二叉树:
() () ()
/ \ / \
() () () ()
/ \ / \
() ()() ()
在上面这棵有 3 3 3 个节点的二叉树中,有 3 3 3 个子树是满二叉树(包括整个树本身,及所有的单个叶子节点);
又例如,
(1)
/ \
(2) (3)
/ \
(4) (5)
在上面这棵有 5 5 5 个节点的二叉树中,有 4 4 4 个子树是满二叉树(包括节点 3 3 3 的子树,以及所有单个叶子节点)。
输入格式
第一行,一个正整数 n n n,表示有根二叉树结点数量。
接下来 n n n 行,每行两个非负整数 l i , r i l_i, r_i li,ri,表示结点 i i i 的左儿子编号和右儿子编号,整数之间以空格分隔。
输出格式
输出一行,一个整数,表示所有子树中满二叉树的数量。
输入输出样例 #1
输入 #1
4
2 3
4 0
0 0
0 0
输出 #1
2
输入输出样例 #2
输入 #2
3
2 3
0 0
0 0
输出 #2
3
说明/提示
数据范围
对于 40 % 40\% 40% 的测试点,保证 1 ≤ n ≤ 500 1 \le n \le 500 1≤n≤500。
对于所有测试点,保证 1 ≤ n ≤ 10 5 1 \le n \le 10^5 1≤n≤105。
题解
本题要求统计一棵有根二叉树中,所有子树(即以每个结点为根的子树)中有多少棵是满二叉树。
满二叉树的定义是:所有叶子深度相同,且除叶子外每个结点都有两个儿子。
我采用后序遍历(DFS)自底向上计算每个结点的信息:
d[x]:以结点x为根的子树的高度(深度)。若为满二叉树,则所有叶子深度相同,高度定义为从根到叶子的边数加 1(即叶子深度为 1)。b[x]:布尔值(用 0/1 表示),标记以x为根的子树是否是满二叉树。
递归过程:
- 叶子结点(
l[x]==0 && r[x]==0):显然是一棵满二叉树,d[x]=1,b[x]=1,答案加 1。 - 非叶子结点:先递归处理左右子树,然后判断:
- 若左、右子树都存在且都是满二叉树(
b[l[x]]==1 && b[r[x]]==1),并且左右子树高度相等(d[l[x]] == d[r[x]]),则当前子树也是满二叉树,高度为d[l[x]]+1,标记b[x]=1,答案加 1。 - 否则当前子树不是满二叉树,
b[x]=0,不增加答案。
- 若左、右子树都存在且都是满二叉树(
注意:如果某个结点只有一个儿子,则不可能成为满二叉树,因为满二叉树要求除叶子外都有两个儿子,所以这种情况直接不满足条件(代码中逻辑隐含了 b[0] 为 0 且 d[0] 为 0,但左/右儿子为 0 时不满足 b[l[x]] && b[r[x]],故不会错误计数)。
最终输出答案 ans。
时间复杂度:每个结点访问一次, O ( n ) O(n) O(n), n ≤ 10 5 n \le 10^5 n≤105 完全可行。
空间复杂度: O ( n ) O(n) O(n)。
带注释的源代码
#include <bits/stdc++.h>
using namespace std;
int n;
int l[100005]; // l[i]:结点 i 的左儿子编号,0 表示不存在
int r[100005]; // r[i]:结点 i 的右儿子编号,0 表示不存在
int d[100005]; // d[i]:以 i 为根的子树高度(若为满二叉树则有效)
int b[100005]; // b[i]:1 表示以 i 为根的子树是满二叉树,0 表示不是
int ans = 0; // 统计满二叉树子树的个数
// 后序遍历结点 x
void dfs(int x) {
// 情况1:x 是叶子结点
if (l[x] == 0 && r[x] == 0) {
d[x] = 1; // 叶子高度为 1
b[x] = 1; // 叶子是满二叉树
ans++; // 计数
return;
}
// 先递归处理左右子树(若存在)
if (l[x] > 0) dfs(l[x]);
if (r[x] > 0) dfs(r[x]);
// 情况2:x 有左右儿子,且左右子树都是满二叉树,且高度相等
// 注意:如果某个儿子不存在,则 b[0] 默认为 0,条件不成立
if (b[l[x]] && b[r[x]] && d[l[x]] == d[r[x]]) {
d[x] = d[l[x]] + 1; // 当前子树高度 = 子树高度 + 1
b[x] = 1; // 当前子树是满二叉树
ans++; // 计数
}
// 否则当前子树不是满二叉树,b[x] 保持为 0(全局默认),不需要额外操作
return;
}
int main() {
cin >> n;
// 读入每个结点的左右儿子
for (int i = 1; i <= n; i++) {
cin >> l[i] >> r[i];
}
// 从根结点 1 开始后序遍历
dfs(1);
// 输出满二叉树子树的个数
cout << ans;
return 0;
}
更多推荐


所有评论(0)