1004 Counting Leaves【PAT (Advanced Level) Practice】

1004 Counting Leaves【PAT (Advanced Level) Practice】

原题链接:预览题目详情 - 1004 Counting Leaves (pintia.cn)

1.题目原文

A family hierarchy is usually presented by a pedigree tree. Your job is to count those family members who have no child.

Input Specification:

Each input file contains one test case. Each case starts with a line containing 0 < N < 100 0<N<100 0<N<100, the number of nodes in a tree, and M M M ( < N <N <N), the number of non-leaf nodes. Then M M M lines follow, each in the format:

ID K ID[1] ID[2] ... ID[K]

where ID is a two-digit number representing a given non-leaf node, K is the number of its children, followed by a sequence of two-digit ID’s of its children. For the sake of simplicity, let us fix the root ID to be 01.

The input ends with N N N being 0. That case must NOT be processed.

Output Specification:

For each test case, you are supposed to count those family members who have no child for every seniority level starting from the root. The numbers must be printed in a line, separated by a space, and there must be no extra space at the end of each line.

The sample case represents a tree with only 2 nodes, where 01 is the root and 02 is its only child. Hence on the root 01 level, there is 0 leaf node; and on the next level, there is 1 leaf node. Then we should output 0 1 in a line.

Sample Input:

2 1
01 1 02

Sample Output:

0 1

2. 题目翻译

家族层次通常由一个家谱树来表示。你的任务是统计那些没有子女的家庭成员的数量。

输入规范:

每个输入文件包含一个测试用例。每个案例都以一行开始,包含 0 < N < 100 0<N<100 0<N<100,树中节点的数量,以及 M M M < N <N <N),非叶节点的数量。然后,接下来是 M M M 行,每行的格式如下:

ID K ID[1] ID[2] ... ID[K]

其中 ID 是表示给定非叶节点的两位数字,K 是其子节点的数量,后面是其子节点的两位数字 ID 的序列。为了简单起见,我们将根节点的 ID 固定为 01

输入以 N N N 为 0 结束。这种情况不准被处理。(就是不用考虑 N N N 0 0 0 的情况,前面也说了 0 < N < 100 0<N<100 0<N<100

输出规范:

对于每个测试用例,你应该从根开始统计那些在每个辈分层次上都没有子女的家庭成员。数字必须以一行的形式打印,用空格分隔,每行末尾不能有额外的空格。

示例案例表示一个只有2个节点的树,其中 01 是根,02 是它唯一的子节点。因此,在根 01 层上,有 0 个叶节点;在下一层上,有 1 个叶节点。然后我们应该输出 0 1 一行。

示例输入:

2 1
01 1 02

示例输出:

0 1

3.解题思路

3.1题目分析

通过输入家谱树结构的描述,统计每个辈分层次上没有子女的家庭成员数量。

输入:

树中节点的数量N	非叶节点的数量M
M行输入:给定非叶节点的两位数字ID(根节点ID固定为 01)		其子节点的数量k	其子节点的两位数字 ID 的序列

输出:

顺次从根节点开始输出每一层没有子节点的结点个数

3.2基本思路

通过构建家谱树并深度优先搜索,统计每个辈分层次上没有子女的家庭成员数量,并按照层次顺序输出结果。

3.3详解步骤

  1. 树节点结构体定义:
    • TreeNode 结构体包含三个成员变量,分别是 Child(指向子节点的指针)、name(节点名称,即两位数字的ID)、Sibling(指向兄弟节点的指针)。
  2. 全局变量定义:
    • Tree T[100]:用于存储树节点的数组,索引表示节点的ID
    • int cnt[99]:用于存储每个辈分层次上没有子女的家庭成员数量。
    • int level, maxlevel:用于追踪递归访问树节点时的层次深度。
  3. 新建树节点函数:
    • new_node 函数用于创建一个新的树节点,分配内存并初始化节点的 ChildSibling 指针为 NULL
  4. 深度优先搜索访问树节点函数:
    • Visit 函数用于递归访问树节点,统计每个辈分层次上没有子女的家庭成员数量。
    • 如果节点没有子女,则增加相应层次的家庭成员数量;否则,递归访问子节点,然后访问兄弟节点。
  5. 主函数:
    • 初始化树节点数组和计数数组。
    • 读取输入,构建树结构。
      • 输入以 NM 开始,分别表示节点数量和非叶节点数量。
      • 随后的输入为非叶节点的信息,包括节点ID、子节点数量和子节点的ID
    • 调用 Visit 函数计算每个辈分层次上没有子女的家庭成员数量。
    • 输出结果:输出每个辈分层次上没有子女的家庭成员数量,按照层次顺序,用空格分隔。

3.4注意要点

4.参考答案

#include <stdio.h>
#include <malloc.h>

// 定义树节点的结构体
typedef struct TreeNode *Tree;
struct TreeNode {
    
    
    Tree Child;      // 子节点
    int name;        // 节点名称
    Tree Sibling;    // 兄弟节点
};

Tree T[100];          // 存储树节点的数组
int cnt[99], level, maxlevel;  // cnt数组用于存储每个辈分层次上没有子女的家庭成员数量,level和maxlevel用于追踪层次深度

// 创建新的树节点
Tree new_node() {
    
    
    Tree temp;
    temp = (Tree)malloc(sizeof(struct TreeNode));
    temp->Child = temp->Sibling = NULL;
    return temp;
}

// 深度优先搜索访问树节点
void Visit(Tree T) {
    
    
    if (!T->Child)
        cnt[level]++;  // 如果节点没有子女,增加相应辈分的家庭成员数量
    else {
    
    
        level++;  // 进入下一层次
        if (level > maxlevel)
            maxlevel = level;  // 更新最大层次深度
        Visit(T->Child);  // 递归访问子节点
        level--;  // 返回上一层次
    }
    if (T->Sibling)
        Visit(T->Sibling);  // 递归访问兄弟节点
}

int main() {
    
    
    int i, j, N, M;
    int id1, id2, k;

    // 初始化树节点数组和计数数组
    for (i = 0; i < 99; i++) {
    
    
        T[i + 1] = NULL;
        cnt[i] = 0;
    }

    level = maxlevel = 0;
    scanf("%d %d", &N, &M);

    // 读取输入,构建树
    T[1] = new_node();
    T[1]->name = 1;
    // 读取输入,构建树结构
    for (i = 0; i < M; i++) {
    
    
        scanf("%d %d", &id1, &k);

        // 如果当前节点不存在,则创建新节点并初始化
        if (!T[id1]) {
    
    
            T[id1] = new_node();
            T[id1]->name = id1;
        }

        // 如果节点有子节点,读取第一个子节点的信息
        if (k) {
    
    
            scanf("%d", &id2);

            // 如果子节点不存在,则创建新节点并初始化
            if (!T[id2]) {
    
    
                T[id2] = new_node();
                T[id2]->name = id2;
            }

            T[id1]->Child = T[id2];  // 设置当前节点的子节点
            id1 = id2;  // 更新当前节点为子节点,用于处理兄弟节点
        }

        // 处理当前节点的兄弟节点
        for (j = 1; j < k; j++) {
    
    
            scanf("%d", &id2);

            // 如果兄弟节点不存在,则创建新节点并初始化
            if (!T[id2]) {
    
    
                T[id2] = new_node();
                T[id2]->name = id2;
            }

            T[id1]->Sibling = T[id2];  // 设置当前节点的兄弟节点
            id1 = id2;  // 更新当前节点为兄弟节点,用于处理下一个兄弟节点
        }
    }

    // 计算每个辈分层次上没有子女的家庭成员数量
    Visit(T[1]);

    // 输出结果
    printf("%d", cnt[0]);
    for (i = 1; i <= maxlevel; i++)
        printf(" %d", cnt[i]);
    printf("\n");

    // 释放内存
    for (i = 0; i < 99; i++) {
    
    
        free(T[i + 1]);
        T[i + 1] = NULL;
        cnt[i] = 0;
    }

    return 0;
}

5.知识拓展

深度优先搜索(Depth-First Search,DFS)

深度优先搜索(Depth-First Search,DFS)是一种用于图和树等数据结构的搜索算法。该算法从起始节点开始,尽可能深地访问每一个可能的分支,直到无法继续为止,然后回溯到上一个节点,继续尝试其他分支。DFS通常使用递归或栈来实现。

以下是深度优先搜索的基本步骤:

  1. 访问起始节点: 将起始节点标记为已访问,并将其加入访问路径。

  2. 递归或栈: 对于起始节点的每个未访问邻居节点,重复上述步骤。可以使用递归调用或者使用栈来追踪节点的访问顺序。

  3. 回溯: 当无法再深入时,回溯到上一个节点,继续尝试其他分支。

  4. 重复: 重复步骤2和步骤3,直到所有可达节点都被访问过。

深度优先搜索在解决迷宫问题、图遍历、拓扑排序等问题中都有广泛应用。

猜你喜欢

转载自blog.csdn.net/weixin_40171190/article/details/134746759