1476. 数叶子结点 PAT1004
5树1 法一弄 用dps 1476. 数叶子结点 PAT1004,深度优先
//1476. 数叶子结点
//
//家庭关系可以用家谱树来表示,给定一个家谱树,你的任务是找出其中没有孩子的成员。
//
//输入格式
//第一行包含一个整数 N表示树中结点总数
//以及一个整数 M表示非叶子结点数。
//
//接下来 M行,每行的格式为:
//
//ID K ID[1] ID[2] ... ID[K]
//
//ID是一个两位数字,表示一个非叶子结点编号,
//K是一个整数,表示它的子结点数,
//接下来的 K个 ID[i]也是两位数字,表示一个子结点的编号。
//
//为了简单起见,我们将根结点固定设为 01。
//
//所有结点的编号即为 01, 02, 03, …, 31, 32, 33, …, N。
//
//输出格式
//输出从根结点开始,自上到下,树的每一层级分别包含多少个叶子节点。
//
//输出占一行,整数之间用空格隔开。
//
//数据范围
//0 < N < 100
//
//输入样例:
//2 1 //2个结点,1个非叶子结点
//01 1 02 //01结点有1个叶子结点是02
//
//输出样例:
//0 1 //第一层有0个叶子结点,第二层有1个叶子结点
//
//样例解释
//该样例表示一棵只有 2
//个结点的树,
//其中 01结点是根,
//而 02结点是其唯一的子节点。
//
//因此,在根这一层级上,存在 0个叶结点;
//在下一个级别上,有 1个叶结点。
//
//所以,我们应该在一行中输出0 1。
//弄夫
#include<iostream>
#include<vector>
using namespace std;
//N所有结点数,M非叶子结点数
int N, M;
//定义vector数组,1号结点的孩子保存再vector[1]中,2号结点的孩子保存在vector[2]中
vector<int>child[100];
//dfs函数维护2个变量:最大层数maxleve,
int maxlevel;//全局遍历默认0
//以及每一层的无孩子结点数num_of_eachlevel[100]
int num_of_eachlevel[100] = {0};
//curID当前结点,curlevel当前的层次
void dfs(int curID,int curlevel) {
//每到一个结点就要进行一个首要的判断
//如果当前的层次比以前去过的层次都要大,
//那么最大层就要更新
if (curlevel > maxlevel) {
maxlevel = curlevel;
}
//接下来 可能进入有孩结点,无孩结点,两种结点操作不同
//如果进入有孩结点,不会更新int num_of_eachlevel[100],
if (child[curID].size() > 0) {
//但是要进行向下的dfs
/*for (auto x : child[curid]) {
dfs(x, curlevel + 1);
}*/
for (int i = 0; i < child[curID].size(); i++) {
dfs(child[curID][i], curlevel + 1);
}
}
//如果进入无孩结点,更新int num_of_eachlevel[当前层次],不会向下dfs,
else {
num_of_eachlevel[curlevel]++;
}
}
int main() {
int i, j, k;
cin >> N >> M;
//初始化:接下去的M行
for (i = 0; i < M; i++) {
//ID是本结点,k是ID有几个孩子
int ID, k;
cin >> ID >> k;
//k个孩子输入进来
while (k--) {
cin >> j;
//ID号结点的孩子保存在vector的child[ID]中
child[ID].emplace_back(j);
}
}
//怎么找出每一行的无孩子结点数---遍历---dfs
//从1号结点,第1层开始dfs遍历
dfs(1, 1);
//输出
for (i = 1; i <= maxlevel; i++) {
//考虑到输出的0 1,1后面没空格
//可以这样做
//第一个输出的0前面没空格,后面输出1之前都要先输出空格
if (i > 1) {
cout << ' ';
}
cout << num_of_eachlevel[i];
}
}
5树1 法二yxc用dfs 1476. 数叶子结点 PAT1004,深度优先
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110;
int n, m;
int h[N], e[N], ne[N], idx;
//cnt[]存储每一层的无孩子结点数量
int cnt[N], max_depth;
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u, int depth)
{
if (h[u] == -1) // 说明u是叶子节点
{
cnt[depth] ++;
max_depth = max(max_depth, depth);
return;
}
//不是叶子结点就遍历(单链表遍历),递归
//~i=-i-1,~i等价于-i-1!=0,当i=-1时,-i-1=0跳出循环
for (int i = h[u]; ~i; i = ne[i])
dfs(e[i], depth + 1);
}
int main()
{
cin >> n >> m;
//memset一般是将一个数组全部初始化为0或1,设为无穷大为0x3f;
memset(h, -1, sizeof h);
//接下来m行
for (int i = 0; i < m; i++)
{
int id, k;
cin >> id >> k;
while (k--)
{
int son;
cin >> son;
add(id, son);
}
}
dfs(1, 0);
//这是pat的输出行末无空格
cout << cnt[0];
for (int i = 1; i <= max_depth; i++) cout << ' ' << cnt[i];
cout << endl;
return 0;
}
5树1 法三网友:bfs 1476. 数叶子结点 PAT1004,深度优先
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 110;
int n, m;
//定义vector数组,1号结点的孩子保存再vector[1]中,2号结点的孩子保存在vector[2]中
vector<int> child[N];
int cnt[N], depth=0;//cnt[]存储每一层的无孩子结点数量
void bfs() {
queue<int> q;
q.push(-1);
while (!q.empty()) {
int s = q.size();
for (int i = 0; i < s; ++i) {
int t = q.front();
q.pop();
if (child[t].empty()) {
cnt[depth]++;
}
else {
for (int i = 0; i < child[t].size(); i++) {
q.push(child[t][i]);
}
//for (auto& son : child[t]) {q.push(son);}//此句效果同上
}
}
depth++;
}
}
int main() {
cin >> n >> m;
while (m--) {
int id, k;
cin >> id >> k;
while (k--) {
int j;
cin >> j;
child[id].push_back(j);
}
}
bfs();
cout << cnt[0];
for (int i = 1; i < depth; i++) cout << " " << cnt[i];
cout << endl;
return 0;
}
5树1 法四网友:dfs 结合弄夫和yxc 1476. 数叶子结点 PAT1004,深度优先
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 110;
int n, m;//n是总结点数,m是非叶子节点数
vector<int> child[N];//1号结点的孩子保存再vector[1]中,2号结点的孩子保存在vector[2]中
int cnt[N], max_depth;//cnt[]存储每一层的无孩子结点数量
void dfs(int k, int depth) {
if (child[k].empty()) {
cnt[depth]++;
max_depth = max(max_depth, depth);
return;
}
for (auto& son : child[k]) {
dfs(son, depth + 1);
}
}
int main() {
cin >> n >> m;
while (m--) {
int id, k;
cin >> id >> k;
while (k--) {
int id_k;
cin >> id_k;
child[id].push_back(id_k);
}
}
dfs(1, 0);
cout << cnt[0];
for (int i = 1; i <= max_depth; i++) cout << " " << cnt[i];
cout << endl;
return 0;
}
1497. 树的遍历 PAT1020
一个二叉树,树中每个节点的权值互不相同。
现在给出它的后序遍历和中序遍历,请你输出它的层序遍历。
输入格式
第一行包含整数 N,表示二叉树的节点数。
第二行包含 N个整数,表示二叉树的后序遍历。
第三行包含 N个整数,表示二叉树的中序遍历。
输出格式
输出一行 N个整数,表示二叉树的层序遍历。
数据范围
1≤N≤30
官方并未给出各节点权值的取值范围,为方便起见,在本网站范围取为 1∼N
输入样例:
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7
输出样例:
4 1 6 3 5 7 2
5树2 法一弄 递归 1497. 树的遍历 PAT1020树的构造 背下来这题!!!常见
//1497. 树的遍历
//
//一个二叉树,树中每个节点的权值互不相同。
//
//现在给出它的后序遍历和中序遍历,请你输出它的层序遍历。
//
//输入格式
//第一行包含整数 N
//,表示二叉树的节点数。
//
//第二行包含 N
//个整数,表示二叉树的后序遍历。
//
//第三行包含 N
//个整数,表示二叉树的中序遍历。
//
//输出格式
//输出一行 N
//个整数,表示二叉树的层序遍历。
//
//数据范围
//1≤N≤30
//官方并未给出各节点权值的取值范围,为方便起见,在本网站范围取为 1∼N
//
//输入样例:
//7
//2 3 1 5 7 6 4 //后序
//1 2 3 4 5 6 7 //中序
//
//输出样例:
//4 1 6 3 5 7 2
#include<iostream>
using namespace std;
int N;//一共几个结点
int Post[40];//存贮后序遍历
int In[40];//存贮中序遍历
struct node {
int value;
node* left, * right;
};
node* makenode(int h1,int t1,int h2,int t2) {//h1是后序遍历队头,t1是尾,h2是中序遍历队头
//找到递归出口
if (h1 > t1)return NULL;
node* p = new node();
p->value = Post[t1];//先找到根,第一轮是4
//2 3 1 5 7 6 4 后序
//1 2 3 4 5 6 7 中序
//第一轮index是4的下标
int index;
for (index = h2; In[index] != Post[t1]; index++) {}
//如何确定第二个参数?
//传入的后序序列和中序序列长度一定相同
//初始是是t2-h2=t1-h1=6
int u = (index - 1 - h2) + h1;
int v = (t1 - 1) - (t2 - index - 1);
p->left = makenode(h1, u, h2, index - 1);
p->right = makenode(v, t1 - 1, index + 1, t2);
return p;
}
int main() {
cin >> N;
int i, j, k;
for (i = 0; i < N; i++) {//后序遍历
cin >> Post[i];
}
for (i = 0; i < N; i++) {//中序遍历
cin >> In[i];
}
node* root = makenode(0,N-1,0,N-1);//根据后序,中序构建树
//层序遍历输出该树
//手写一个队列,而没有用stl的队列
node* Queue[40];
int head = 0; int tail = 0;//手写队列的头好尾
Queue[tail++] = root;
while (head < tail) {
//应对PAT输出末尾无空格,考虑成输出空格数据空格数据,只有第一个数据前面无空格
if (head != 0)cout << ' ';
node* p = Queue[head++];
cout << p->value;
if (p->left) {
Queue[tail++] = p->left;
}
if (p->right) {
Queue[tail++] = p->right;
}
}
}
默写,加了些自己手写时候的思路
#include<iostream>
#include<queue>
using namespace std;
int N;
int Post[40];
int In[40];
struct node {
int value;
node* left, * right;
};
node* makenode(int h1, int t1, int h2, int t2) {
//递归出口
if (h1 > t1) {
return NULL;
}
//找到根结点(后序序列最右边的),并且new出新节点,赋值
node* p = new node();
p->value = Post[t1];
//开始递归建造左右子树,关键是如何确定函数的4个参数
//先序后序找根,中序分左右
//根就是后序序列最右边的
//找到根在中序序列的下标!
int index = 0;
while (In[index] != Post[t1]) {
index++;
}//最终index=Post[t1],即根结点(后序序列最右边的结点)在中序序列的下标
int u = (index - 1 - h2) + h1;//index-1=u-h1
int v = (t1 - 1) - (t2 - index - 1);//t2-index-1=t1-1-v
p->left = makenode(h1, u, h2, index - 1);
p->right = makenode(v, t1 - 1, index + 1, t2);
return p;
}
int main() {
cin >> N;
for (int i = 0; i < N; i++) {
cin >> Post[i];
}
for (int i = 0; i < N; i++) {
cin >> In[i];
}
node* root = makenode(0, N - 1, 0, N - 1);
//层序遍历输出
//根结点放入队列中,
//出队,输出,如果有左孩子,左孩子入队,有右孩子则由孩子入队
queue<node* > q;
q.push(root);
while (q.empty() == false) {
node* s = q.front();
q.pop();
cout << s->value << ' ';
if (s->left != NULL) {
q.push(s->left);
}
if (s->right != NULL) {
q.push(s->right);
}
}
return 0;
}
5树2 法二yxc 递归 1497. 树的遍历 PAT1020树的构造
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
const int N = 40;
int n;
int postorder[N];//后序遍历 序列
int inorder[N];//中序遍历 序列
unordered_map<int, int> l, r, pos;//pos是中序遍历下标,pos[4]代表中序遍历中数字4的数组下标
int q[N];
//il是中序遍历的左端点,ir是中序遍历的右端点,pl是后序遍历的左端点
int build(int il, int ir, int pl, int pr)
{
int root = postorder[pr];//首先根结点是后序遍历的右端点
int k = pos[root];//k是中序遍历中根结点(上面后序遍历右端点)的下标
//il<k则左子树存在,l[root]是根的左儿子
if (il < k) l[root] = build(il, k - 1, pl, pl + (k - 1 - il));
if (k < ir) r[root] = build(k + 1, ir, pl + (k - 1 - il) + 1, pr - 1);
return root;
}
void bfs(int root)//宽搜
{
int hh = 0, tt = 0;//手写队列,hh是头,tt是尾
q[0] = root;
while (hh <= tt)
{
int t = q[hh++];
if (l.count(t)) q[++tt] = l[t];
if (r.count(t)) q[++tt] = r[t];
}
cout << q[0];
for (int i = 1; i < n; i++) cout << ' ' << q[i];
cout << endl;
}
int main()
{
cin >> n;
for (int i = 0; i < n; i++) cin >> postorder[i];//输入后序遍历序列
for (int i = 0; i < n; i++){
cin >> inorder[i];//输入中序遍历序列
pos[inorder[i]] = i;//存贮下中序遍历 每个数字的 下标是多少
}
//构建二叉树,前两参数是中序的头尾下标,后俩参数是后序
int root = build(0, n - 1, 0, n - 1);
bfs(root);
return 0;
}
1498. 最深的根 PAT1021
5树3 法一弄 深搜1498. 最深的根 PAT1021
//1498. 最深的根
//
//一个无环连通图可以被视作一个树。
//
//树的高度取决于所选取的根节点。
//
//现在,你要找到可以使得树的高度最大的根节点。
//
//它被称为最深的根。
//
//输入格式
//第一行包含整数 N,表示节点数量。
//
//节点编号为 1∼N。
//
//接下来 N−1行,每行包含两个整数,表示两个节点之间存在一条边。
//
//输出格式
//输出最深的根的节点编号。
//
//如果最深的根不唯一,则按照从小到大的顺序,将它们依次输出,每个占一行。
//
//如果给定的图不是树,输出 Error : K components,
//其中 K是图中连通分量的数量。
//
//数据范围
//1≤N≤104
//输入样例1:
//5
//1 2
//1 3
//1 4
//2 5
//输出样例1:
//3
//4
//5
//输入样例2:
//5
//1 3
//1 4
//2 5
//3 4
//输出样例2:
//Error : 2 components
#include<iostream>
#include<vector>
#include<set>
using namespace std;
vector<int>v[10001];
int visited[10001];//用于标记连通分量的结点是否已经遍历过
//用于判断几个连通分量的dfs
void dfs(int cur) {//cur是当前结点的编号
//及时止损,已访问就返回
if (visited[cur]) {
return;
}
//标记已访问
visited[cur] = 1;
//遍历当前结点的所有邻居
for (int i : v[cur]) {
dfs(i);
}
/*for (int i = 0; i < v[cur].size(); i++) {
dfs(i);
}*/
}
vector<int>farthest_point;
int maxdis = 0;
void dfs2(int cur, int depth) {
//及时止损
if (visited[cur])return;
//标记已访问
visited[cur] = 1;
if (depth > maxdis) {
maxdis = depth;
farthest_point.clear();
farthest_point.emplace_back(cur);
}
else if (depth == maxdis) {
farthest_point.emplace_back(cur);
}
for (int i : v[cur]) {
dfs2(i, depth + 1);
}
}
set<int>final_ans;//存放最终答案,set有自动排序功能
int main() {
int N, i, j, k;
cin >> N;
for (i = 1; i < N; i++) {//输入N-1条边
cin >> j >> k;//j和k相互连通,用vector表示
v[j].emplace_back(k);
v[k].emplace_back(j);
}
//先判断 有几个连通分量,随便找个入口dfs 就能遍历到该联通分量所有的点,用到visited[]
//先初始化visited[]=0//N-1条边所以i从1开始
for (i = 1; i <= N; i++) visited[i] = 0;
//用于标记有几个连通分量
int count = 0;
//比如1,3,4连在一起,2单独,先从1遍历,会遍历到3,4,都标记已访问,这算是一个连通分量,
//再从2开始遍历,标记已访问,算是一个连通分量。
//在从3开始遍历,因为已访问过,不遍历,4同理
//故一共2个连通分量
for (i = 1; i <= N; i++) {
if (!visited[i]) {
count++;
dfs(i);
}
}
if (count > 1) {
//多个连通分量
cout << "Error : " << count << " components";
}
else if (count == 1) {
//一个连通分量,是个树
//此时寻找最深的根
for (i = 1; i <= N; i++) visited[i] = 0;//初始化visit[]
dfs2(1, 0);
for (int each : farthest_point) {
final_ans.insert(each);
}
farthest_point.clear();
maxdis = 0;
for (i = 1; i <= N; i++) visited[i] = 0;//初始化visit[]
dfs2(*final_ans.begin(), 0);//只要头尾各dfs2遍历一次就行,不用每个结点都dfs2一遍么?
//经过这两次头尾的dfs2,得到两个farthest_point数组,都汇聚到set中
//此时,set中保存了左边和右边的红点
//set有去重复功能和排序功能
for (int each : farthest_point) {
final_ans.insert(each);
}
for (int each : final_ans) {
cout << each << '\n';
}
}
}
题目大意:给定图的n个结点和n-1条边,要求判断该图是不是树,如果是,输出最深的根节点,如果不是,输出有几个连通分量
思路大致如下:
1.题目保证图有n个顶点,n-1条边,由此可知要么是树,要么成环。
2.对图进行dfs1搜索,将搜索到的结点标记,如果dfs搜索后仍然有没有标记的(即没有搜索到)结点,就说明没有搜索到的结点和已经搜索到的结点没有道路,即属于两个不同的连通分量,再对没有搜索到的结点进行一次dfs1搜索,重复执行以上操作直到所有结点都被访问过。同时统计有几个连通分量
3.对于是树的情况(连通分量个数为1必是树),对其进行另一种dfs2搜索,找出最深的根节点,需要进行两次dfs2操作,第一次找出离1结点最远的结点,第二次找出离1结点最远的结点的最远的结点,将两次找出的结点存到set容器中,最后输出即可。
4.dfs2操作要定义一个全局变量,用来表示当前最深距离,以此为依据更新最深结点。
写题心得(总结):
1.以前没有接触过此类题目,把edge的意思理解成了边缘,实际上就是边的意思
2.知道了如何用vector二维数组存放图,并且对其进行深度优先搜索(DFS)
3.对树的概念加深(连通分量数量为1且结点为n且边为n-1的图必定是树),如果不是,必然有连通分量成环。
4.fill函数可以将容器内一段区间的元素赋为相同值,比memset函数更强,因为赋值类型更加丰富
其他
5树5 1550. 完全二叉搜索树 PAT甲级真题1064
1550. 完全二叉搜索树
二叉搜索树 (BST) 递归定义为具有以下属性的二叉树:
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值
- 若它的右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值
- 它的左、右子树也分别为二叉搜索树
完全二叉树 (CBT) 定义为除最深层外的其他层的结点数都达到最大个数,最深层的所有结点都连续集中在最左边的二叉树。
现在,给定 N个不同非负整数,表示 N 个结点的权值,用这 N 个结点可以构成唯一的完全二叉搜索树。
请你输出该完全二叉搜索树的层序遍历。
输入格式
第一行包含整数 N,表示结点个数。
第二行包含 N个不同非负整数,表示每个结点的权值。
输出格式
共一行,输出给定完全二叉搜索树的层序遍历序列。
数据范围
1≤N≤1000,
结点权值不超过 2000
输入样例:
10
1 2 3 4 5 6 7 8 9 0
输出样例:
6 3 8 1 5 7 9 0 2 4
//1550. 完全二叉搜索树
//
//二叉搜索树(BST) 递归定义为具有以下属性的二叉树:
//
//若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值
//若它的右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值
//它的左、右子树也分别为二叉搜索树
//完全二叉树(CBT) 定义为除最深层外的其他层的结点数都达到最大个数,最深层的所有结点都连续集中在最左边的二叉树。
//
//现在,给定 N个不同非负整数,
//表示 N个结点的权值,用这 N个结点可以构成唯一的完全二叉搜索树。
//
//请你输出该完全二叉搜索树的层序遍历。
//
//输入格式
//第一行包含整数 N,表示结点个数。
//
//第二行包含 N
//个不同非负整数,表示每个结点的权值。
//
//输出格式
//共一行,输出给定完全二叉搜索树的层序遍历序列。
//
//数据范围
//1≤N≤1000
//,
//结点权值不超过 2000。
//
//输入样例:
//10
//1 2 3 4 5 6 7 8 9 0
//
//输出样例:
//6 3 8 1 5 7 9 0 2 4
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include <unordered_set>
#include <set>
#include <unordered_map>
#include<algorithm>
#include<deque>
#include <math.h>
#include<cstdio>
using namespace std;
const int N = 1010;
//yxc
class Solution {
public:
int n;
int w[N];//表示权值
int tr[N];//表示结点
//全局共用一个k,所以加引用&
void dfs(int u, int& k) //中序遍历
{
//递归左子树
if (u * 2 <= n) {
dfs(u * 2, k);
}
//左子树转到右子树时,给该下标(tr[u])的阶段赋值(赋予权值w[k]),
//结点下标是按照层序遍历1,2,3,4,5,6,7,8这么下来的
tr[u] = w[k++];
//递归右子树
if (u * 2 + 1 <= n) {
dfs(u * 2 + 1, k);
}
}
void func() {
cin >> n;//读入序列
for (int i = 0; i < n; i++) {//读入权值
cin >> w[i];
}
sort(w, w + n);//得到二叉树中序遍历的结果
//不需要写代码构建完全二叉树,数组1-n就是完全二叉树,
//2x就是左孩子,2x+1是右孩子,x/2是父节点
int k = 0;
dfs(1, k);
//输出,记得pat最后一个数字的输出后面没空格
cout << tr[1];
for (int i = 2; i <= n; i++) {
cout << ' ' << tr[i];
}
cout << endl;
}
};
//弄
class Solution2 {
public:
//8 4 9 2 10 5 1 6 3 7 //下标中序遍历(堆数组的物理下标的中序遍历)
//0 1 2 3 4 5 6 7 8 9 //对输入的数组元素的排序
//6 3 8 1 5 7 9 0 2 4 //堆数组中的实际存贮的值
//说白了就是把输入的序列排个序,然后存入堆数组的 中序遍历下标当中。最后输出对数组的值。
int N;
int a[1010];//堆数组
vector<int> inorder;//inorder保存了中序遍历时的下标
vector<int> data;
void inordertraverse(int i) {//中序遍历,传入的i是下标
//如果下标超过了节点数,那就回退(递归终止条件)
if (i > N) return;
//先遍历左子树,左孩子下标是i*2
inordertraverse(i * 2);
//inorder保存了中序遍历时的堆数组的物理下标
inorder.emplace_back(i);
//遍历右子树
inordertraverse(i * 2 + 1);
}
void func() {
int i, j, k;
cin >> N;
inordertraverse(1);//从下标1的结点开始中序遍历
//输入各结点的权值,并从小到大排序
for (i = 0; i < N; i++) {
cin >> j;
data.emplace_back(j);
}
sort(data.begin(), data.end());
for (int i = 0; i < N; i++) {
//inorder[i]是 二叉树中序遍历 时候 经历的 堆数组的 物理下标
//data[i]是我输入的权值,从小到大排序
a[inorder[i]] = data[i];
}
//最后层序遍历输出,其实就是按序 输出堆数组的值,可用yxc方法
//这里用了层序遍历输出(就练练层序吧)
queue<int>q;
q.push(1);
while (q.size()) {
i = q.front();
q.pop();
if (i > 1)cout << ' ';//控制空格,第一个不输出空格,后面的先输出空格再输出值
cout << a[i];
if (i * 2 <= N)q.push(i * 2);
if (i * 2 + 1 <= N)q.push(i * 2 + 1);
}
}
};
int main() {
Solution s;
s.func();
return 0;
}
5树6 1576. 再次树遍历 PAT甲级真题1086
1576. 再次树遍历
通过使用栈可以以非递归方式实现二叉树的中序遍历。
例如,假设遍历一个如下图所示的 66 节点的二叉树(节点编号从 11 到 66)。
则堆栈操作为:push(1); push(2); push(3); pop(); pop(); push(4); pop(); pop(); push(5); push(6); pop(); pop()。
我们可以从此操作序列中生成唯一的二叉树。
你的任务是给出这棵树的后序遍历。
输入格式
第一行包含整数 N,表示树中节点个数。
树中节点编号从 1 到 N。
接下来 2N2� 行,每行包含一个栈操作,格式为:
Push X
,将编号为 X 的节点压入栈中。Pop
,弹出栈顶元素。
输出格式
输出这个二叉树的后序遍历序列。
数据保证有解,数和数之间用空格隔开,末尾不能有多余空格。
数据范围
1≤N≤30
输入样例:
6
Push 1
Push 2
Push 3
Pop
Pop
Push 4
Pop
Pop
Push 5
Push 6
Pop
Pop
输出样例:
3 4 2 6 5 1
//1576. 再次树遍历
//
//通过使用栈可以以非递归方式实现二叉树的中序遍历。
//
//例如,假设遍历一个如下图所示的 6节点的二叉树(节点编号从 1到 6)。
//
//则堆栈操作为:push(1); push(2); push(3); pop(); pop(); push(4); pop(); pop(); push(5); push(6);
//pop(); pop()。
//
//我们可以从此操作序列中生成唯一的二叉树。
//
//你的任务是给出这棵树的后序遍历。
//
//3.png
//
//输入格式
//第一行包含整数 N,表示树中节点个数。
//
//树中节点编号从 1到 N。
//
//接下来 2N行,每行包含一个栈操作,格式为:
//
//Push X,将编号为 X的节点压入栈中。
//Pop,弹出栈顶元素。
//
//输出格式
//输出这个二叉树的后序遍历序列。
//
//数据保证有解,数和数之间用空格隔开,末尾不能有多余空格。
//
//数据范围
//1≤N≤30
//
//输入样例:
//6
//Push 1
//Push 2
//Push 3
//Pop
//Pop
//Push 4
//Pop
//Pop
//Push 5
//Push 6
//Pop
//Pop
//
//输出样例:
//3 4 2 6 5 1
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include <unordered_set>
#include <set>
#include <unordered_map>
#include<algorithm>
#include<deque>
#include <math.h>
#include<cstdio>
using namespace std;
const int N = 40;
//法一:push就是先序遍历,pop就是中序遍历,于是可以先序中序建树,建完树后,后续遍历输出即可
class Solution {
public:
int N;
int Pre[40];
int In[40];
struct node {
int value;
node* left, * right;
};
node* makenode(int h1, int t1, int h2, int t2) {
//递归出口
if (h1 > t1) {
return NULL;
}
//找到根结点(后序序列最右边的),并且new出新节点,赋值
node* p = new node();
p->value = Pre[h1];
//开始递归建造左右子树,关键是如何确定函数的4个参数
//先序后序找根,中序分左右
//根就是后序序列最右边的
//找到根在中序序列的下标!
int index = 0;
while (In[index] != Pre[h1]) {
index++;
}//最终index=Post[t1],即根结点(后序序列最右边的结点)在中序序列的下标
int u = index - h2 + h1;//index-1-h2=u-h1-1;u=index-1-h2+h1+1=index-h2+h1
int v = t1 - t2 + index + 1;//t2-index-1=t1-v;v=t1-(t2-index-1)=t1-t2+index+1
p->left = makenode(h1 + 1, u, h2, index - 1);
p->right = makenode(v, t1, index + 1, t2);
return p;
}
void func() {
}
};
//yxc方法:上一个是push操作,则是左儿子,上一个是pop,则是右儿子
//由此重建出二叉树,再后续遍历输出即可
//左儿子和右儿子,相当于做了个映射,
//l[1]=2代表1号结点 的左孩子 是2
int l[40], r[40]; //这俩必须放在Solution2之外,不然会报错,不知道为何
class Solution2 {
public:
void dfs(int u, int root)
{
if (!u) return;
dfs(l[u], root);//这个用l[],r[]数组模拟二叉树存储,并且深度遍历的方法,emm
dfs(r[u], root);
cout << u;
if (u != root) cout << ' ';
}
void func() {
int n;//结点总数
cin >> n;
int root;//存入根节点,不一定是树根
int last = 0;//上一个结点
int type;//上一个操作类型,push 还是 pop
stack<int> stk;
for (int i = 0; i < n * 2; i++){//n个结点有2n个操作
string op;
cin >> op;
if (op == "Push"){
int x;
cin >> x;//读取当前结点的值x
if (!last) { //第一次push(没有上一个结点,即!last=true),root=x
root = x;
}
else{//不是第一次push
if (type == 0) {//上一次操作是push,则x是上一个结点last的左孩子
l[last] = x;
}
else {
r[last] = x;//否则是上一个结点last的右孩子
}
}
stk.push(x);//x入栈
last = x;//更新上一个结点为x
type = 0; // 表示push
}
else{//如果是pop操作
last = stk.top();//更新上一个结点 是栈顶元素
stk.pop(); //出栈
type = 1; // 表示pop
}
}
dfs(root, root);//第二个root是为了控制pat最后输出无空格
}
};
//法三:修改yxc,将树的存储方式从root,l[40],r[40]改成结构体TreeNode
class Solution3 {
public:
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode* left, TreeNode* right) : val(x), left(left), right(right) {}
};
void dfs(TreeNode* u, TreeNode* root) {
if (!u) return;
dfs(u->left, root);
dfs(u->right, root);
cout << u->val;
if (u != root) cout << ' ';
}
void func() {
int n;//结点总数
cin >> n;
TreeNode* root = new TreeNode();
TreeNode* last = NULL;
int type;//上一个操作类型,push 还是 pop
stack<TreeNode*> stk;
for (int i = 0; i < n * 2; i++) {//n个结点有2n个操作
string op;
cin >> op;
if (op == "Push") {
int x;
cin >> x;//读取当前结点的值x
TreeNode* t = new TreeNode(x);
if (last==NULL) { //第一次push(没有上一个结点)
root = t;
}
else {//不是第一次push
if (type == 0) {//上一次操作是push,则x是上一个结点last的左孩子
//l[last] = x;
last->left = t;
}
else {
//r[last] = x;//否则是上一个结点last的右孩子
last->right = t;
}
}
stk.push(t);//x入栈
last = t;//更新上一个结点为x
type = 0; // 表示push
}
else {//如果是pop操作
last = stk.top();//更新上一个结点 是栈顶元素
stk.pop(); //出栈
type = 1; // 表示pop
}
}
dfs(root, root);//第二个root是为了控制pat最后输出无空格
}
};
int main() {
Solution3 s;
s.func();
}
5树7 1589. 构建二叉搜索树 PAT甲级真题1099
1589. 构建二叉搜索树
二叉搜索树 (BST) 递归定义为具有以下属性的二叉树:
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值
- 若它的右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值
- 它的左、右子树也分别为二叉搜索树
给定二叉树的具体结构以及一系列不同的整数,只有一种方法可以将这些数填充到树中,以使结果树满足 BST 的定义。
请你输出结果树的层序遍历。
示例如图 11 和图 22 所示。
输入格式
第一行包含一个正整数 N,表示树的结点个数。
所有结点的编号为 0∼N−1,并且编号为 00 的结点是根结点。
接下来 N 行,第 i 行(从 00 计数)包含结点 i 的左右子结点编号。如果该结点的某个子结点不存在,则用 −1−1 表示。
最后一行,包含 N 个不同的整数,表示要插入树中的数值。
输出格式
输出结果树的层序遍历序列。
数据范围
1≤N≤100
输入样例:
9
1 6
2 3
-1 -1
-1 4
5 -1
-1 -1
7 -1
-1 8
-1 -1
73 45 11 58 82 25 67 38 42
输出样例:
58 25 82 11 38 67 45 73 42
//1576. 再次树遍历
//
//通过使用栈可以以非递归方式实现二叉树的中序遍历。
//
//例如,假设遍历一个如下图所示的 6节点的二叉树(节点编号从 1到 6)。
//
//则堆栈操作为:push(1); push(2); push(3); pop(); pop(); push(4); pop(); pop(); push(5); push(6);
//pop(); pop()。
//
//我们可以从此操作序列中生成唯一的二叉树。
//
//你的任务是给出这棵树的后序遍历。
//
//3.png
//
//输入格式
//第一行包含整数 N,表示树中节点个数。
//
//树中节点编号从 1到 N。
//
//接下来 2N行,每行包含一个栈操作,格式为:
//
//Push X,将编号为 X的节点压入栈中。
//Pop,弹出栈顶元素。
//
//输出格式
//输出这个二叉树的后序遍历序列。
//
//数据保证有解,数和数之间用空格隔开,末尾不能有多余空格。
//
//数据范围
//1≤N≤30
//
//输入样例:
//6
//Push 1
//Push 2
//Push 3
//Pop
//Pop
//Push 4
//Pop
//Pop
//Push 5
//Push 6
//Pop
//Pop
//
//输出样例:
//3 4 2 6 5 1
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include <unordered_set>
#include <set>
#include <unordered_map>
#include<algorithm>
#include<deque>
#include <math.h>
#include<cstdio>
using namespace std;
const int N = 40;
//法一:push就是先序遍历,pop就是中序遍历,于是可以先序中序建树,建完树后,后续遍历输出即可
class Solution {
public:
int N;
int Pre[40];
int In[40];
struct node {
int value;
node* left, * right;
};
node* makenode(int h1, int t1, int h2, int t2) {
//递归出口
if (h1 > t1) {
return NULL;
}
//找到根结点(后序序列最右边的),并且new出新节点,赋值
node* p = new node();
p->value = Pre[h1];
//开始递归建造左右子树,关键是如何确定函数的4个参数
//先序后序找根,中序分左右
//根就是后序序列最右边的
//找到根在中序序列的下标!
int index = 0;
while (In[index] != Pre[h1]) {
index++;
}//最终index=Post[t1],即根结点(后序序列最右边的结点)在中序序列的下标
int u = index - h2 + h1;//index-1-h2=u-h1-1;u=index-1-h2+h1+1=index-h2+h1
int v = t1 - t2 + index + 1;//t2-index-1=t1-v;v=t1-(t2-index-1)=t1-t2+index+1
p->left = makenode(h1 + 1, u, h2, index - 1);
p->right = makenode(v, t1, index + 1, t2);
return p;
}
void func() {
}
};
//yxc方法:上一个是push操作,则是左儿子,上一个是pop,则是右儿子
//由此重建出二叉树,再后续遍历输出即可
//左儿子和右儿子,相当于做了个映射,
//l[1]=2代表1号结点 的左孩子 是2
int l[40], r[40]; //这俩必须放在Solution2之外,不然会报错,不知道为何
class Solution2 {
public:
void dfs(int u, int root)
{
if (!u) return;
dfs(l[u], root);//这个用l[],r[]数组模拟二叉树存储,并且深度遍历的方法,emm
dfs(r[u], root);
cout << u;
if (u != root) cout << ' ';
}
void func() {
int n;//结点总数
cin >> n;
int root;//存入根节点,不一定是树根
int last = 0;//上一个结点
int type;//上一个操作类型,push 还是 pop
stack<int> stk;
for (int i = 0; i < n * 2; i++){//n个结点有2n个操作
string op;
cin >> op;
if (op == "Push"){
int x;
cin >> x;//读取当前结点的值x
if (!last) { //第一次push(没有上一个结点,即!last=true),root=x
root = x;
}
else{//不是第一次push
if (type == 0) {//上一次操作是push,则x是上一个结点last的左孩子
l[last] = x;
}
else {
r[last] = x;//否则是上一个结点last的右孩子
}
}
stk.push(x);//x入栈
last = x;//更新上一个结点为x
type = 0; // 表示push
}
else{//如果是pop操作
last = stk.top();//更新上一个结点 是栈顶元素
stk.pop(); //出栈
type = 1; // 表示pop
}
}
dfs(root, root);//第二个root是为了控制pat最后输出无空格
}
};
//法三:修改yxc,将树的存储方式从root,l[40],r[40]改成结构体TreeNode
class Solution3 {
public:
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode* left, TreeNode* right) : val(x), left(left), right(right) {}
};
void dfs(TreeNode* u, TreeNode* root) {
if (!u) return;
dfs(u->left, root);
dfs(u->right, root);
cout << u->val;
if (u != root) cout << ' ';
}
void func() {
int n;//结点总数
cin >> n;
TreeNode* root = new TreeNode();
TreeNode* last = NULL;
int type;//上一个操作类型,push 还是 pop
stack<TreeNode*> stk;
for (int i = 0; i < n * 2; i++) {//n个结点有2n个操作
string op;
cin >> op;
if (op == "Push") {
int x;
cin >> x;//读取当前结点的值x
TreeNode* t = new TreeNode(x);
if (last==NULL) { //第一次push(没有上一个结点)
root = t;
}
else {//不是第一次push
if (type == 0) {//上一次操作是push,则x是上一个结点last的左孩子
//l[last] = x;
last->left = t;
}
else {
//r[last] = x;//否则是上一个结点last的右孩子
last->right = t;
}
}
stk.push(t);//x入栈
last = t;//更新上一个结点为x
type = 0; // 表示push
}
else {//如果是pop操作
last = stk.top();//更新上一个结点 是栈顶元素
stk.pop(); //出栈
type = 1; // 表示pop
}
}
dfs(root, root);//第二个root是为了控制pat最后输出无空格
}
};
int main() {
Solution3 s;
s.func();
}
5树8 1592. 反转二叉树 PAT甲级真题1102
1592. 反转二叉树
以下是来自 Max Howell @twitter
的内容:
谷歌:我们的百分之九十的工程师都使用你编写的软件,但是你连在白板上反转二叉树都做不到,还是滚吧。
现在,请你证明你会反转二叉树。
输入格式
第一行包含一个整数 N,表示树的结点数量。
所有结点编号从 0到 N−1。
接下来 N 行,每行对应一个 0∼N−1 的结点,给出该结点的左右子结点的编号,如果该结点的某个子结点不存在,则用 −
表示。
输出格式
输出反转后二叉树的层序遍历序列和中序遍历序列,每个序列占一行。
相邻数字之间用空格隔开,末尾不得有多余空格。
数据范围
1≤N≤10
输入样例:
8
1 -
- -
0 -
2 7
- -
- -
5 -
4 6
输出样例:
3 7 2 6 4 0 5 1
6 5 7 4 3 2 0 1
//1592. 反转二叉树
//以下是来自 Max Howell @twitter 的内容:
//
//谷歌:我们的百分之九十的工程师都使用你编写的软件,但是你连在白板上反转二叉树都做不到,还是滚吧。
//现在,请你证明你会反转二叉树。
//
//输入格式
//第一行包含一个整数 N,表示树的结点数量。
//
//所有结点编号从 0到 N−1。
//
//接下来 N行,每行对应一个 0∼N−1的结点,给出该结点的左右子结点的编号,
//如果该结点的某个子结点不存在,则用 − 表示。
//
//输出格式
//输出反转后二叉树的层序遍历序列和中序遍历序列,每个序列占一行。
//
//相邻数字之间用空格隔开,末尾不得有多余空格。
//
//数据范围
//1≤N≤10
//输入样例:
//8
//1 -
//--
//0 -
//2 7
//- -
//--
//5 -
//4 6
//输出样例:
//3 7 2 6 4 0 5 1
//6 5 7 4 3 2 0 1
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include <unordered_set>
#include <set>
#include <unordered_map>
#include<algorithm>
#include<deque>
#include <math.h>
#include<cstdio>
using namespace std;
const int N = 15;
//法一 yxc
int n;
int l[N], r[N];
int q[N];
bool has_father[N];//用于求根节点,没有父结点的点就是根结点
class Solution {
public:
void dfs_reverse(int u)
{
if (u == -1) return;
dfs_reverse(l[u]);
dfs_reverse(r[u]);
swap(l[u], r[u]);
}
void bfs(int root)
{
int hh = 0, tt = 0;
q[0] = root;
while (hh <= tt)
{
int t = q[hh++];
if (l[t] != -1) q[++tt] = l[t];
if (r[t] != -1) q[++tt] = r[t];
}
cout << q[0];
for (int i = 1; i < n; i++) cout << ' ' << q[i];
cout << endl;
}
void dfs(int u, int& k)
{
if (u == -1) return;
dfs(l[u], k);
cout << u;
if (++k != n) cout << ' ';
dfs(r[u], k);
}
void func() {
cin >> n;
memset(l, -1, sizeof l);
memset(r, -1, sizeof r);
for (int i = 0; i < n; i++)
{
char lc, rc;
cin >> lc >> rc;
if (lc != '-') {
l[i] = lc - '0';
has_father[l[i]] = true;//i有左孩子,则l[i]即i的左孩子有父节点,l[i]肯定不是根结点
}
if (rc != '-') {
r[i] = rc - '0';
has_father[r[i]] = true;
}
}
//找出根结点
int root = 0;
while (has_father[root]) {
root++;
}
//深度遍历翻转树
dfs_reverse(root);
//层序输出
bfs(root);
//后序输出,k是pat用来控制最后一个输出无空格的
int k = 0;
dfs(root, k);
}
};
法二 网友
//struct node{
// int l, r;
// int data;
//}tr[N];
int n;
//bool st[N];
//class Solution2 {
//public:
// void level(int u)
// {
// queue<int>q;
// q.push(u);
// while (q.size())
// {
// auto t = q.front();
// q.pop();
// cout << tr[t].data << ' ';
// if (~tr[t].l)q.push(tr[t].l);
// if (~tr[t].r)q.push(tr[t].r);
// }
// }
// void in(int u)
// {
// if (u == -1)return;
// in(tr[u].l);
// cout << tr[u].data << ' ';
// in(tr[u].r);
// }
// void func() {
// cin >> n;
// for (int i = 0; i < n; i++)
// {
// char a, b;
// cin >> a >> b;
//
// if (b != '-'){
// tr[i].l = b - '0';
// st[tr[i].l] = true;
// }
// else tr[i].l = -1;
//
// if (a != '-'){
// tr[i].r = a - '0';
// st[tr[i].r] = true;
// }
// else tr[i].r = -1;
//
// tr[i].data = i;
// }
// int root = -1;
// for (int i = 0; i < n; i++)//找到根结点
// if (!st[i])
// {
// root = i;
// break;
// }
//
//
// level(root);//好家伙,不翻转树,直接层序遍历输出翻转后的 层序遍历结果
// cout << endl;
// in(root);
// }
//};
//法三:我修改yxc
struct Node {
int val;
int left;
int right;
}node[N];
//int n;
//bool has_father[N];//用于求根节点,没有父结点的点就是根结点
class Solution3 {
public:
void dfs_reverse(int u) {
if (u == -1)return;
dfs_reverse(node[u].left);
dfs_reverse(node[u].right);
swap(node[u].left, node[u].right);
}
vector<int> v;
void levelorder(int u) {
queue<int> q;
q.push(u);
while (q.empty() == false) {
int i = q.front();
q.pop();
v.push_back(i);
if (node[i].left != -1)q.push(node[i].left);
if (node[i].right != -1)q.push(node[i].right);
}
for (int i = 0; i < v.size(); i++) {
if (i != 0)cout << ' ';
cout << v[i];
}
}
void inorder(int u,int& k) {
if (u == -1)return;
inorder(node[u].left, k);
cout << u;
if (++k != n) cout << ' ';
inorder(node[u].right, k);
}
void func() {
cin >> n;
for (int i = 0; i < n; i++) {
char l, r;
cin >> l >> r;
if (l == '-') {
node[i].left = -1;
}
else {
int j = l - '0';
node[i].left = j;
has_father[j] = true;
}
if (r == '-') {
node[i].right = -1;
}
else {
int t = r - '0';
node[i].right = t;
has_father[t] = true;
}
}
//找出根结点
int root = 0;
while (has_father[root] == true) {
root++;
}
dfs_reverse(root);
levelorder(root);
cout << endl;
int k = 0;
inorder(root, k);
}
};
int main() {
Solution3 s;
s.func();
return 0;
}
//这题难在怎么构建二叉树呢
//node[i].left = l - '0';
1600. 完全二叉树
给定一个树,请你判断它是否是完全二叉树。
输入格式
第一行包含整数 N,表示树的结点个数。
树的结点编号为 0∼N−1。
接下来 N 行,每行对应一个结点,并给出该结点的左右子结点的编号,如果某个子结点不存在,则用 -
代替。
输出格式
如果是完全二叉树,则输出 YES
以及最后一个结点的编号。
如果不是,则输出 NO
以及根结点的编号。
数据范围
1≤N≤20
输入样例1:
9
7 8
- -
- -
- -
0 1
2 3
4 5
- -
- -
输出样例1:
YES 8
输入样例2:
8
- -
4 5
0 6
- -
2 3
- 7
- -
- -
输出样例2:
NO 1
//1600. 完全二叉树
//
//给定一个树,请你判断它是否是完全二叉树。
//
//输入格式
//第一行包含整数 N,表示树的结点个数。
//
//树的结点编号为 0∼N−1。
//
//接下来 N行,每行对应一个结点,并给出该结点的左右子结点的编号,如果某个子结点不存在,则用 - 代替。
//
//输出格式
//如果是完全二叉树,则输出 YES 以及最后一个结点的编号。
//
//如果不是,则输出 NO 以及根结点的编号。
//
//数据范围
//1≤N≤20
//输入样例1:
//9
//7 8
//- -
//--
//- -
//0 1
//2 3
//4 5
//- -
//--
//输出样例1:
//YES 8
//输入样例2:
//8
//- -
//4 5
//0 6
//- -
//2 3
//- 7
//- -
//--
//输出样例2:
//NO 1
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include <unordered_set>
#include <set>
#include <unordered_map>
#include<algorithm>
#include<deque>
#include <math.h>
#include<cstdio>
using namespace std;
const int N = 25;
int l[N];
int r[N];
//法一:yxc
class Solution {
public:
int maxk, maxid;//记录最后一个结点的编号
bool has_father[N] = { false };
//若结点下标最大值maxk(堆数组下标,从1到n) 等于 结点个数n,就是完全二叉树。否则不是。
//maxid是
void dfs(int u, int k)
{
if (u == -1) return;
if (k > maxk)
{
maxk = k;
maxid = u;
}
dfs(l[u], k * 2);
dfs(r[u], k * 2 + 1);
}
void func() {
int n = 0;
//memset(l, -1, sizeof l);
//memset(r, -1, sizeof r);
fill(l, l + N, -1);
fill(r, r + N, -1);
cin >> n;
for (int i = 0; i < n; i++)
{
string a, b;
cin >> a >> b;
if (a != "-") {
l[i] = stoi(a);//stoi就是string to int, 反之则是 to_string()函数
has_father[l[i]] = true;
}
if (b != "-") {
r[i] = stoi(b);
has_father[r[i]] = true;
}
}
int root = 0;
while (has_father[root]) root++;//找到根结点
//这个1是堆数组第一元素的下标,这样才能左儿子是2x,右儿子是2x+1
//而root是根结点编号(结点编号从0到n-1)这个是用来递归遍历的
dfs(root, 1);
//若结点下标最大值k 等于 结点个数n,就是完全二叉树。否则不是。
if (maxk == n) printf("YES %d\n", maxid);
else printf("NO %d\n", root);
}
};
//法二:我对yxc修改下存储结构 用struct
struct Node{
int val;
int left;
int right;
}node[N];
class Solution2 {
public:
int maxk = 0;
int maxid = 0;
void dfs(int u, int k) {
if (u == -1)return;
if (k > maxk) {
maxk = k;
maxid = u;
}
dfs(node[u].left, 2 * k);
dfs(node[u].right, 2 * k + 1);
}
bool has_father[N] = { false };
void func() {
int n = 0;
cin >> n;
//构建二叉树
for (int i = 0; i < n; i++) {
string l, r;
cin >> l >> r;
if (l == "-") {
node[i].left = -1;
}
else {
node[i].left = stoi(l);
has_father[stoi(l)] = true;
}
if (r == "-") {
node[i].right = -1;
}
else {
node[i].right = stoi(r);
has_father[stoi(r)] = true;
}
}
//寻找根节点啊
int root = 0;
while (has_father[root] == true)root++;
dfs(root, 1);
//若maxk(堆数组时的最大下标值)等于n(结点个数),则是完全二叉树,输出yes和最后一个结点的编号id
if (maxk == n) {
cout << "YES" << ' ' << maxid;
}
else {
cout << "NO" << ' ' << root;
}
}
};
int main(){
Solution2 s;
s.func();
return 0;
}
1605. 二叉搜索树最后两层结点数量
二叉搜索树 (BST) 递归定义为具有以下属性的二叉树:
- 若它的左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值
- 它的左、右子树也分别为二叉搜索树
将一系列数字按顺序插入到一个空的二叉搜索树中,然后,请你计算结果树的最低两层的结点个数。
输入格式
第一行包含整数 N,表示插入数字序列包含的数字个数。
第二行包含 N个整数,表示插入数字序列。
输出格式
以如下格式,在一行中,输出结果树的最后两层的结点数:
n1 + n2 = n
n1
是最底层结点数量,n2
是倒数第二层结点数量,n
是它们的和。
数据范围
1≤N≤1000,
−1000≤−1000≤ 插入数字 ≤1000≤1000。
输入样例:
9
25 30 42 16 20 20 35 -5 28
输出样例:
2 + 4 = 6
//1605. 二叉搜索树最后两层结点数量
//
//二叉搜索树(BST) 递归定义为具有以下属性的二叉树:
//
//若它的左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值
//若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值
//它的左、右子树也分别为二叉搜索树
//将一系列数字按顺序插入到一个空的二叉搜索树中,然后,请你计算结果树的最低两层的结点个数。
//
//输入格式
//第一行包含整数 N,表示插入数字序列包含的数字个数。
//
//第二行包含 N个整数,表示插入数字序列。
//
//输出格式
//以如下格式,在一行中,输出结果树的最后两层的结点数:
//
//n1 + n2 = n
//n1 是最底层结点数量,n2 是倒数第二层结点数量,n 是它们的和。
//
//数据范围
//1≤N≤1000,−1000≤插入数字 ≤1000。
//
//输入样例:
//9
//25 30 42 16 20 20 35 - 5 28
//
//输出样例:
//2 + 4 = 6
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include <unordered_set>
#include <set>
#include <unordered_map>
#include<algorithm>
#include<deque>
#include <math.h>
#include<cstdio>
using namespace std;
const int N = 1010;
//注意本题是小于等于放左边,大于放右边,
//pat其他题目都是小于放左边,大于等于放右边。
//法一;yxc
int n;
int l[N], r[N];
int v[N];//表示权值多少
int idx;//结点编号,从1开始,自己定义的,题目没给,是输入时候的顺序
int cnt[N];//cnt[1]=2表示第一层有俩结点
int max_depth;
class Solution {
public:
void insert(int& u, int w){
if (!u){//如果u是0,创建新结点,这也是递归出口
u = ++idx;//编号+1,作为新节点的编号
v[u] = w;//新结点权值赋为w
}
else if (w <= v[u]) {//如果新结点的权值小于当前结点
insert(l[u], w);//就插入当前结点u的左孩子去
}
else {
insert(r[u], w);
}
}
void dfs(int u, int depth)
{
if (!u) return;//递归出口,u=0就return
cnt[depth] ++;//当前深度的节点数++
max_depth = max(max_depth, depth);//更新最大深度
dfs(l[u], depth + 1);//往左边递归,深度加一
dfs(r[u], depth + 1);
}
void func() {
cin >> n;
int root = 0;//根节点
//随着输入的权值w,构建该二叉搜索树
for (int i = 0; i < n; i++) {
int w;
cin >> w;//读入权值
insert(root, w);//插入二叉搜索树中
}
//通过dfs找到最大深度,每个深度有多少结点
//root是根结点,root=1,0是层数
dfs(root, 0);
int n1 = cnt[max_depth];
int n2 = cnt[max_depth - 1];
printf("%d + %d = %d\n", n1, n2, n1 + n2);
}
};
//法二,修改
struct Node {
int val;
int left;
int right;
}node[N];
class Solution2 {
public:
void insert(int& u, int w) {
if (u == 0) {
u = ++idx;
node[u].val = w;
}
else if (w <= node[u].val) {
insert(node[u].left, w);//下一层函数的u的值改变了,这里的node[u].left也跟着变,因为是引用&u
}
else {
insert(node[u].right, w);
}
}
void dfs(int u, int depth)
{
if (!u) return;//递归出口,u=0就return
cnt[depth] ++;//当前深度的节点数++
max_depth = max(max_depth, depth);//更新最大深度
dfs(node[u].left, depth + 1);//往左边递归,深度加一
dfs(node[u].right, depth + 1);
}
void func() {
cin >> n;
//构建这个二叉树
int root = 0;
for (int i = 0; i < n; i++) {
int w;
cin >> w;
//root第一次是零,之后就一直是1了,因为insert有引用,下一层函数改了,这里的root也跟着改
insert(root, w);
}
dfs(root, 0);
int n1 = cnt[max_depth];
int n2 = cnt[max_depth - 1];
printf("%d + %d = %d\n", n1, n2, n1 + n2);
}
};
int main() {
Solution2 s;
s.func();
return 0;
}
5树011 1609. 前序和后序遍历 PAT甲级真题1119
1609. 前序和后序遍历
假设一个二叉树上所有结点的权值都互不相同。
我们可以通过后序遍历和中序遍历来确定唯一二叉树。
也可以通过前序遍历和中序遍历来确定唯一二叉树。
但是,如果只通过前序遍历和后序遍历,则有可能无法确定唯一二叉树。
现在,给定一组前序遍历和后序遍历,请你输出对应二叉树的中序遍历。
如果树不是唯一的,则输出任意一种可能树的中序遍历即可。
输入格式
第一行包含整数 N�,表示结点数量。
第二行给出前序遍历序列。
第三行给出后序遍历序列。
一行中的数字都用空格隔开。
输出格式
首先第一行,如果树唯一,则输出 Yes
,如果不唯一,则输出 No
。
然后在第二行,输出树的中序遍历。
注意,如果树不唯一,则输出任意一种可能的情况均可。
数据范围
1≤N≤30
输入样例1:
7
1 2 3 4 6 7 5
2 6 7 4 5 3 1
输出样例1:
Yes
2 1 6 4 7 3 5
输入样例2:
4
1 2 3 4
2 4 3 1
输出样例2:
No
2 1 3 4
//1609. 前序和后序遍历
//
//假设一个二叉树上所有结点的权值都互不相同。
//
//我们可以通过后序遍历和中序遍历来确定唯一二叉树。
//
//也可以通过前序遍历和中序遍历来确定唯一二叉树。
//
//但是,如果只通过前序遍历和后序遍历,则有可能无法确定唯一二叉树。
//
//现在,给定一组前序遍历和后序遍历,请你输出对应二叉树的中序遍历。
//
//如果树不是唯一的,则输出任意一种可能树的中序遍历即可。
//
//输入格式
//第一行包含整数 N,表示结点数量。
//
//第二行给出前序遍历序列。
//
//第三行给出后序遍历序列。
//
//一行中的数字都用空格隔开。
//
//输出格式
//首先第一行,如果树唯一,则输出 Yes,如果不唯一,则输出 No。
//
//然后在第二行,输出树的中序遍历。
//
//注意,如果树不唯一,则输出任意一种可能的情况均可。
//
//数据范围
//1≤N≤30
//输入样例1:
//7
//1 2 3 4 6 7 5
//2 6 7 4 5 3 1
//输出样例1:
//Yes
//2 1 6 4 7 3 5
//
//输入样例2:
//4
//1 2 3 4
//2 4 3 1
//输出样例2:
//No
//2 1 3 4
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include <unordered_set>
#include <set>
#include <unordered_map>
#include<algorithm>
#include<deque>
#include <math.h>
#include<cstdio>
using namespace std;
const int N = 40;
int n;
int pre[N], post[N];
class Solution {
public:
int dfs(int l1, int r1, int l2, int r2, string& in){
if (l1 > r1) return 1;
if (pre[l1] != post[r2]) return 0;
int cnt = 0;
for (int i = l1; i <= r1; i++) // 枚举左子树包含的节点数量
{
string lin, rin;
int lcnt = dfs(l1 + 1, i, l2, l2 + i - l1 - 1, lin);
int rcnt = dfs(i + 1, r1, l2 + i - l1 - 1 + 1, r2 - 1, rin);
if (lcnt && rcnt)
{
in = lin + to_string(pre[l1]) + ' ' + rin;
cnt += lcnt * rcnt;
if (cnt > 1) break;
}
}
return cnt;
}
void func() {
cin >> n;
for (int i = 0; i < n; i++) cin >> pre[i];
for (int i = 0; i < n; i++) cin >> post[i];
string in;
int cnt = dfs(0, n - 1, 0, n - 1, in);
if (cnt > 1) puts("No");
else puts("Yes");
in.pop_back();//去除末尾空格
cout << in << endl;//输出中序遍历
}
};
int main(){
Solution s;
s.func();
return 0;
}
5树012 1620. Z 字形遍历二叉树 PAT甲级真题1127
1620. Z 字形遍历二叉树
假设一个二叉树上各结点的权值互不相同。
我们就可以通过其后序遍历和中序遍历来确定唯一二叉树。
请你输出该二叉树的 Z字形遍历序列----也就是说,从根结点开始,逐层遍历,第一层从右到左遍历,第二层从左到右遍历,第三层从右到左遍历,以此类推。
例如,下图所示二叉树,其 Z字形遍历序列应该为:1 11 5 8 17 12 20 15
。
输入格式
第一行包含整数 N,表示二叉树结点数量。
第二行包含 N 个整数,表示二叉树的中序遍历序列。
第三行包含 N 个整数,表示二叉树的后序遍历序列。
输出格式
输出二叉树的 Z 字形遍历序列。
数据范围
1≤N≤30
输入样例:
8
12 11 20 17 1 15 8 5
12 20 17 11 15 8 5 1
输出样例:
1 11 5 8 17 12 20 15
//1620. Z 字形遍历二叉树
//
//假设一个二叉树上各结点的权值互不相同。
//
//我们就可以通过其后序遍历和中序遍历来确定唯一二叉树。
//
//请你输出该二叉树的 Z
//字形遍历序列----也就是说,从根结点开始,逐层遍历,第一层从右到左遍历,
//第二层从左到右遍历,第三层从右到左遍历,以此类推。
//
//例如,下图所示二叉树,其 Z字形遍历序列应该为:1 11 5 8 17 12 20 15。
//
//输入格式
//第一行包含整数 N,表示二叉树结点数量。
//
//第二行包含 N个整数,表示二叉树的中序遍历序列。
//
//第三行包含 N个整数,表示二叉树的后序遍历序列。
//
//输出格式
//输出二叉树的 Z字形遍历序列。
//
//数据范围
//1≤N≤30
//输入样例:
//8
//12 11 20 17 1 15 8 5
//12 20 17 11 15 8 5 1
//输出样例:
//1 11 5 8 17 12 20 15
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include <unordered_set>
#include <set>
#include <unordered_map>
#include<algorithm>
#include<deque>
#include <math.h>
#include<cstdio>
using namespace std;
const int N = 40;
//法一 yxc
class Solution {
public:
int n;
unordered_map<int, int> l, r, pos;
int in[N], post[N];
int q[N];
int build(int il, int ir, int pl, int pr)
{
int root = post[pr];
int k = pos[root];
if (il < k) l[root] = build(il, k - 1, pl, pl + k - 1 - il);
if (k < ir) r[root] = build(k + 1, ir, pl + k - 1 - il + 1, pr - 1);
return root;
}
void bfs(int root)
{
int hh = 0, tt = 0;
q[0] = root;
int step = 0;
while (hh <= tt)
{
int head = hh, tail = tt;
while (hh <= tail)
{
int t = q[hh++];
if (l.count(t)) q[++tt] = l[t];
if (r.count(t)) q[++tt] = r[t];
}
if (++step % 2) reverse(q + head, q + tail + 1);
}
}
void func() {
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> in[i];
pos[in[i]] = i;
}
for (int i = 0; i < n; i++) cin >> post[i];
int root = build(0, n - 1, 0, n - 1);
bfs(root);
cout << q[0];
for (int i = 1; i < n; i++) cout << ' ' << q[i];
cout << endl;
}
};
//法一:我,后序中序建树,然后层序遍历放进vector<vector<int>>,
struct Node {
int val;
int left;
int right;
}node[N];
int n;
unordered_map<int, int>pos;//用于寻找根节点在中序序列中的下标
int in[N], post[N];
queue<int>q;
vector<vector<int>> v;
int k = 0;
vector<int> v2;
string s = "";
int u;
class Solution2 {
public:
//l1是后序左,r1是后序右,l2是中序左,r2是中序右
int build(int l1, int r1, int l2, int r2) {
if (l1 > r1 || l2 > r2) return 0;//这是递归退出条件1
int root = post[r1];//根节点是后序序列最右边的
int index = pos[root];
//这个if也是递归退出条件,和上面的条件1选一个即可,原理一样,
//一个是进入之前判断,一个是进入之后判断
if (l2 <= index - 1) {
node[root].left = build(l1, index - 1 - l2 + l1, l2, index - 1);
}
if (index + 1 <= r2) {
node[root].right = build(r2 - 1 - (r2 - (index + 1)), r1 - 1, index + 1, r2);
}
return root;
}
void bfs(int root) {
q.push(root);
while (q.empty() == false) {
int size = q.size();
for (int i = 0; i < size; i++) {
u = q.front(); q.pop();
v2.push_back(u);
if (node[u].left != 0)q.push(node[u].left);
if (node[u].right != 0)q.push(node[u].right);
}
k++;
if (k % 2 == 1) {
reverse(v2.begin(), v2.end());
}
v.emplace_back(v2);
v2.clear();
}
}
void func() {
//完成输入
cin >> n;
for (int i = 1; i <= n; i++) { cin >> in[i], pos[in[i]] = i;}
for (int i = 1; i <= n; i++)cin >> post[i];
//中序后序建树
int root = build(1, n, 1, n);
//层序遍历输出
bfs(root);
for (int i = 0; i < v.size(); i++) {
for (int j = 0; j < v[i].size(); j++) {
cout << v[i][j] << ' ';
s = s + to_string(v[i][j]) + ' ';
}
}
s.pop_back();//去除最后一个空格
cout << s;
}
};
//网友
class Solution3 {
public:
int n;
int l[N], r[N], max_depth;
vector<int>cnt[N];
int inorder[N], postorder[N];
unordered_map<int, int>pos;
int build(int il, int ir, int pl, int pr)
{
if (il > ir)return 0;
int root = postorder[pr];
int k = pos[root];
l[root] = build(il, k - 1, pl, pl + k - 1 - il);
r[root] = build(k + 1, ir, pl + k - il, pr - 1);
return root;
}
void dfs(int u, int depth)
{
if (u == 0)return;
max_depth = max(max_depth, depth);
cnt[depth].push_back(u);
dfs(l[u], depth + 1);
dfs(r[u], depth + 1);
}
void func() {
cin >> n;
for (int i = 1; i <= n; i++)cin >> inorder[i], pos[inorder[i]] = i;
for (int i = 1; i <= n; i++)cin >> postorder[i];
int root = build(1, n, 1, n);
dfs(root, 1);
for (int i = 1; i <= max_depth; i++)
{
auto& v = cnt[i];
if (i % 2)
for (int i = v.size() - 1; i >= 0; i--)cout << v[i] << ' ';
else
for (int i = 0; i < v.size(); i++)cout << v[i] << ' ';
}
}
};
int main() {
Solution2 s;
s.func();
return 0;
}