目录看这里大哥们!
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)参考的大哥们链接
- 写在后面:才po出两张自己的photos,希望暑假可以做完155道题!
po出155+张photos!~