PTA甲级刷题1004 Counting Leaves(详细注释版+思路总结)

PAT甲级刷题笔记(C++)

Hello!朋友们俺也来写博客啦~
最近在开始刷PAT的甲级真题,欢迎大噶一起来讨论还有互相监督刷题吖~后面也会陆续更新更多题目的刷题思路和完整代码!让我们冲冲冲吖!
在这里插入图片描述

  • 写在前面:希望大家都能向着光走~

前言(树的定义和基本术语)

“ 学过数据结构的同学直接跳过叭~这里写的很基础!因为我也刚学哈哈哈 ”

(1)树的定义

树(tree)的一种定义方式是递归的方法。定义如下:
一棵树是由一个或多个结点的集合T,若这个集合是空集,称为空树;若不是空集,则:

  • 有且仅有一个特定的称为根root的结点,此结点无前驱结点。
  • 根结点以外的其他结点可分为m(m>0)个互不相交的有限集合T1,T2,T3,…,Tm,其中每个集合本身又是一棵树,称为根的子树subtree。如下图贾家族谱中,根结点为祖先,其余元素被分为以贾源贾演为根的两个互不相交的子集T1,T2(即两棵子树),以此类推。
    在这里插入图片描述

(2)基本术语

  • 结点:代表树中的一个数据元素。如上图所示,每个人都是一个结点,包括数据本身以及若干指向其孩子(即子树)的若干分支。
  • 结点的度:指该结点的子树的个数。如贾代善的度为3。
  • 叶子结点leaf node:度为0的结点(即其没有子树或称没有孩子)。如第6层的贾兰、巧姐、贾蓉,第5层的林黛玉等均为叶子结点。
  • 分支结点non-leaf node:度不为0的结点,也称为非终端结点。
  • 树的层数:树的根所在结点的层数为1,层数往下递增。如贾珍所在的层数是5。
  • 树的深度:树中结点的最大层数称为树的深度(也称高度)。如图所示贾家族谱树的深度为6。
  • 孩子结点:某结点的孩子,即该结点的各子树的根。
  • 双亲结点:某结点的前驱结点,与孩子结点相反的概念。
  • 兄弟结点:有相同双亲的孩子称为兄弟结点。如贾敏,贾政,贾赦
  • 祖先结点:从根结点到该结点的所有双亲结点,都是此结点的祖先结点。如贾敏,贾政,贾赦他们的祖先结点相同,包括祖先,贾源,贾代善
  • 子孙结点:与祖先结点相反的概念。如贾代善的子孙结点有贾敏,贾政,贾赦
  • 森林:零棵或有限棵互不相交的树的集合称为森林。若把图中家谱树的祖先去掉,那么所看到的就是由两棵构成的森林。
  • 有序树和无序树:如果树中结点的各子树从左到右是有次序的(即位置不能互换),那么这样的树称为有序树;否则是无序树。

1004 Counting Leaves(树搜索)

(1)题目

  • 英语原题
    在这里插入图片描述

  • 中文意译
    家族的层级关系通常用一棵系谱树表示。你的工作是统计出从根结点开始各层没有孩子的家庭成员(即统计出包括根结点在内的各层叶结点个数)。

    • 示例输入

    第1行:2 1
    ① 2:N (0<N<100),家谱树中结点的个数。
    ② 1:M (M<N),家谱树中非叶结点的个数。

    注:如果大噶对树的定义不是很清楚,可以先看前言,已经学过数据结构的同学就直接跳过前言叭

    第2行:01 1 02
    1)第一个非叶结点的序号01(根结点序号默认为01)2)该非叶结点的孩子数 3)该孩子的序号为02

    第3行~第X行(每一行的格式如下,共有M行):
    1)非叶结点的序号 2)该非叶结点的孩子个数(即结点的度K) 3)各个孩子的序号 (共K个)

    • 示例输出

    输出:0 1
    ① 0:第一层(根结点所在层)的叶结点个数
    ② 1:第二层的叶结点个数
    ……

(2)代码和注释

  • 实现方式②【测试率100%】
// 【思路2:树的搜索,树本身的定义即是利用递归,那该题的难点:划分层数则应该也想到递归,从最顶层根结点(第一层)推起,其子结点的层数=双亲结点层数+1】
// 本题通过自定义结构体进行存储,逻辑层次会更加清晰和明了,不容易定义太多有关联性的变量后把自己绕晕
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<string.h>

using namespace std;

int result[205];
struct Node
{
    int level;//当前节点所在的等级
    int flag;//0没有孩子,1是有孩子
    int father;//父节点
};

int main()
{
    struct Node nodes[205];
    int n,m,i,j;
    int nowNode,nowNodeNumber,childNode;
    int maxLevel=1;
    cin>>n>>m;

    //初始化
    for (i = 0; i <= n; i++)
    {
        nodes[i].level = 0;
        nodes[i].flag = 0;
        nodes[i].father = 0;
    }
    nodes[1].level = 1;

    //输入并保存关系
    //nodes数组的下标位置对应结点的序号
    while (m--)
    {
        cin>>nowNode;
        cin>>nowNodeNumber;

        if(nowNodeNumber != 0)
        nodes[nowNode].flag = 1;

        while (nowNodeNumber--)
        {
            cin>>childNode;
            //保存自己的父亲是谁,但是目前大部分结点的父亲等级仍未知道,仅知道根结点node[1]的等级即level=1,因此后面需要通过递归来求得其他结点的等级
            nodes[childNode].father = nowNode;
        }
    }
    
    // 该题的难点--划分层数
    // 开始划分层数(即等级),i代表序号,从顶层开始遍历读取的结构体数组nodes,若找到某结点的父亲序号是i,由于结构体数组的下标与序号对应,则该结点的等级=父结点nodes[i]+1
    for (i = 1; i <= n; i++)
    {
        for (j = 1; j <= n; j++)
        {
            //如果有一个点的父亲标识是自己,那么它就是你的儿子,那么他的等级,应该是你的等级+1
             if(nodes[j].father == i)
             {
                 nodes[j].level = nodes[i].level + 1;
             }
        }
    }

    //查询每一个等级有多少个没有孩子的点,记录在result数组中
    for (i = 1; i <= n; i++)
    {
        if(nodes[i].flag != 1 && nodes[i].level > 0)
            result[nodes[i].level]++;
        //记录最大的等级,用于最后的输出
        if(nodes[i].level > maxLevel)
            maxLevel = nodes[i].level;
    }
    for (i = 1; i < maxLevel; i++)
    {
        cout<<result[i]<<" ";
    }
    cout<<result[i];
    return 0;  
}

你可能会问思路①在哪里?还在脑子里,代码能力没跟上我的思路呜呜
-----------------------目前先贴半成品代码----------------------------
再过两天要是我还没写出来,我就贴思路~懂的大哥就来捞捞我!!!

  • 实现方式①【这个只成功了一个sample】
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>

using namespace std;

int N, M;
vector<int> result;
int i, j, tmp_col, tmp_row, tmp_subtree, sum;

int main()
{
    cin >> N >> M;
    int alltree[N+1][N+1];
    int ziptree[N+1];
    //initial
    // 相对于定义时直接初始化赋值,fill的填充能保证每个元素都被填充到,比初始化赋值更加安全可靠
//     int alltree[N+1][N+1]={0};
//     int ziptree[N+1]={0};
    fill(alltree[0],alltree[0]+(N+1)*(N+1),0);//设置为0
    fill(ziptree,ziptree+N+1,0);

    if (N==1)
    {
        result.push_back(1);
        cout << result[0];
        return 0;
    }
    else
    {
        result.push_back(0);
        cout << result[0];
    }

    for(i=0;i<M;i++)
    {
        cin >> tmp_row >> tmp_subtree >> tmp_col;
//         cout << tmp_row << ' ' << tmp_subtree << ' ' << tmp_col; // 测试用途
        alltree[tmp_row][tmp_col]=1;
        ziptree[tmp_col]+=1;
        alltree[tmp_row][0]=tmp_subtree;
    }
    //因为序号是从01开始的
    tmp_row=1;
    sum=0;
    for(i=1;i<=N;i++)
    {
        tmp_row+=alltree[i][0];
        result[i]=0;
        if(alltree[i][0]!=0)
        {
            for(j=1;j<=tmp_row;j++)
            {
                if (ziptree[j]==1)
                {
                    result[i]+=1;
//                     cout << result[i] << endl;
                }
            }
            sum+=result[i-1];
            result[i]-=sum;
//             cout << result[i] << endl;
        }
    }

//     vector<int>::iterator it;
//     for (it=result.begin();it!=result.end();it++)
//         cout<<' '<<*it;
    
    for (i=1;i<=(N-M);i++)
    {
        cout<<' '<<result[i];
    }
    
    return 0;
/*  测试用途,看读取数据正不正确
    cout << "alltree" << endl;
    for(i=0;i<=N;i++)
    {
        for(j=0;j<=N;j++)
        {
            cout << alltree[i][j] << endl;
        }
    }
    cout << "alltree" << endl;
    
    cout << "ziptree" << endl;
    for(i=0;i<=N;i++)
    {
        cout << ziptree[i] << endl;
    }
    cout << "ziptree" << endl;
*/
}

(3)归纳与反思

  • 该题的重要思想

(1)选取好变量,来读入具有层级关系的数据
(2)如何划分等级,子结点.Level = 父结点.Level + 1,再结合递归的思想,从上往下推。

(4)参考的大哥们链接

LinkinStar-PAT1004

  • 写在后面:才po出两张自己的photos,希望暑假可以做完155道题!
    po出155+张photos!~
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值