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 1n500

对于所有测试点,保证 1 ≤ n ≤ 10 5 1 \le n \le 10^5 1n105

题解

本题要求统计一棵有根二叉树中,所有子树(即以每个结点为根的子树)中有多少棵是满二叉树。
满二叉树的定义是:所有叶子深度相同,且除叶子外每个结点都有两个儿子。

我采用后序遍历(DFS)自底向上计算每个结点的信息:

  • d[x]:以结点 x 为根的子树的高度(深度)。若为满二叉树,则所有叶子深度相同,高度定义为从根到叶子的边数加 1(即叶子深度为 1)。
  • b[x]:布尔值(用 0/1 表示),标记以 x 为根的子树是否是满二叉树。

递归过程:

  1. 叶子结点l[x]==0 && r[x]==0):显然是一棵满二叉树,d[x]=1b[x]=1,答案加 1。
  2. 非叶子结点:先递归处理左右子树,然后判断:
    • 若左、右子树都存在且都是满二叉树(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 n105 完全可行。
空间复杂度 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;
}
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐