PAT甲级备考——二叉树、堆、树的遍历
题目
【1115】递归构造二叉搜索树,层次遍历,dfs
【1127】中序后序确定,层序遍历
【1119】前序后序转中序
【1110】判断是否是满二叉树
【1102】翻转二叉树,dfs,树的遍历,递归层序遍历(🌳)
【1106】dfs+树的遍历(🌳)
【1130】迭代中序遍历,输出字符串、dfs
【1138】前序中序直接转后序,无需构造树
【1143】二叉搜索树,左节点小于该节点,右节点大于等于该节点
【1099】已知二叉搜索树形状,插入数值,其中序遍历就是数值的升序排列
【1094】dfs 树的遍历计算每层节点的数目 dfs(int index, int level)(🌳)
【1086】前序中序直接转后序,无需构造树(😀)
【1064】二叉完全搜索树,value顺序是中序,直接生成下标值转层序
【1043】二叉搜索树前序判断是否是BST,且输出后序遍历结果
【1155】
【1151】
【1147】
【1135】红黑树
【1066】
【1020】二叉树,后序中序转层序
【1090】树的遍历、dfs(🌳)(😀)
【1079】dfs bfs 树的遍历(🌳)
【1053】树的遍历(🌳)
【1004】(🌳)
注意: 二叉搜索树中序遍历就是节点值的顺序排列
【1115】递归构造二叉搜索树,层次遍历
1115 Counting Nodes in a BST (30 分)先构建二叉搜索树,节点的左节点的值小于等于该节点的值,节点的右节点的值大于该节点的值;接着分别利用迭代法和递归法,bfs遍历二叉搜索树,并计算每一层的节点数目和构建二叉搜索树时:先判断根节点是否为空,若为空则生成新的节点,值为value;若不为空,则若value小于等于根节点的值,则递归构造左子树;若value大于根节点的值,则递归构造右子树。
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
vector<int> level_num;
struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x): val(x), left(nullptr), right(nullptr) {}
};
TreeNode* buildTree(TreeNode* root, int value){
if(root==NULL){
root = new TreeNode(value);
}else if( value <= root->val){
root->left = buildTree(root->left, value);
}else if( value > root->val){
root->right = buildTree(root->right, value);
}
return root;
}
// 迭代法 广度优先遍历
void bfs(TreeNode* root){
if(root==NULL) return;
queue<TreeNode*> que;
que.push(root);
level_num.push_back(1);
while(! que.empty()){
int size = que.size(), number = 0;
for(int i=0; i<size; i++){
TreeNode* node = que.front();
if(node->left){
que.push(node->left);
number += 1;
}
if(node->right){
que.push(node->right);
number += 1;
}
que.pop();
}
level_num.push_back(number);
}
}
// 递归法 深度优先遍历
vector<int> nums(1000);
int maxDepth = -1;
void dfs2(TreeNode* root, int depth){
if(root == NULL){// 如果遍历到叶子节点之后的空节点,更新最大深度的值
maxDepth = max(maxDepth, depth);
return;
}
nums[depth]++; // 记录每层节点数的数组加一
bfs2(root->left, depth+1); // 这里+1隐含着回溯
bfs2(root->right, depth+1);
}
int main(){
int N, value;
cin>>N;
// 构建二叉搜索树
TreeNode* root = NULL;
for(int i=0; i<N; i++){
scanf("%d", &value);
root = buildTree(root, value);
}
// 方法1: 非递归法
// 层次遍历二叉搜索树,计算每层的节点数目
bfs(root);
// 输出倒数第一层和倒数第二层的节点数目,及数目和
int n1 = level_num[level_num.size()-2], n2 = level_num[level_num.size()-3];
printf("%d + %d = %d\n",n1, n2, n1+n2);
// 方法2: 递归深度优先遍历
dfs2(root,0);
int n1 = nums[maxDepth-1], n2 = nums[maxDepth-2];
printf("%d + %d = %d\n",n1, n2, n1+n2);
}
【1127】中序后序确定,层序遍历
【1127】 ZigZagging on a Tree (30 分)先根据中序和后序遍历唯一确定二叉树,再层序遍历输出。
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder, int il, int ir, int pl, int pr){
if(pr - pl == -1) return NULL;
int rootValue = postorder[pr];
TreeNode* root = new TreeNode(rootValue);
if(pr-pl == 0)return root;
int rootIndex;
for(rootIndex=il; rootIndex<=ir; rootIndex++){
if(inorder[rootIndex] == rootValue) break;
}
int new_in_ll = il;
int new_in_lr = rootIndex - 1;
int new_in_rl = rootIndex + 1;
int new_in_rr = ir;
int new_post_ll = pl;
int new_post_lr = pl + new_in_lr - new_in_ll;
int new_post_rl = pl + new_in_lr - new_in_ll + 1;
int new_post_rr = pr - 1;
root->left = buildTree(inorder, postorder, new_in_ll, new_in_lr, new_post_ll, new_post_lr);
root->right = buildTree(inorder, postorder, new_in_rl, new_in_rr, new_post_rl, new_post_rr);
return root;
}
int main(){
int N;
scanf("%d", &N);
vector<int> inorder(N);
vector<int> postorder(N);
for(int i=0; i<N; i++){
scanf("%d", &inorder[i]);
}
for(int j=0; j<N; j++){
scanf("%d", &postorder[j]);
}
// 根据中序顺序和后序顺序构建二叉树
TreeNode* root = buildTree(inorder, postorder, 0, inorder.size()-1, 0, postorder.size()-1);
// 层序遍历变种
if (root==NULL) return 0;
queue<TreeNode*> que;
que.push(root);
int depth = 0;
while(!que.empty()){
depth += 1;
int size = que.size();
vector<int> level(size);
for(int i=0; i<size; i++){
TreeNode* node = que.front();
level[i] = node->val;
que.pop();
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
if(depth%2==0){
for(int j=0; j<level.size(); j++) printf("%d%s", level[j], (que.empty() && j==level.size()-1)?"\n":" ");
}else{
for(int j=level.size()-1; j>=0; j--) printf("%d%s", level[j], (que.empty() && j==0)?"\n":" ");
}
}
return 0;
}
【1119】前序后序转中序
【1119】 Pre- and Post-order Traversals (30 分)根据前序遍历和后序遍历不一定可以确定唯一的二叉树;如果能够确定,则输出Yes,并输出中序遍历的结果;如果不能确定,输出No,并输出其中一个中序遍历的结果。重点:
1. 根据前序和后序生成中序
2. 判断是否唯一的条件(是一个结点可能是根的左孩⼦也有可能是根的右孩⼦,如果发现了⼀个⽆法确定的状态,置judge=false)
#include<iostream>
#include<vector>
using namespace std;
vector<int> in, pre, post;
bool judge = true;
void getIn(int prel, int prer, int postl, int postr){
if(prel == prer){ // 如果是叶子节点,则在中序数组中加入这个节点
in.push_back(pre[prel]);
return;
}
if(pre[prel] == post[postr]){ // 如果前序的头==后序的尾
int rootIndex; // 找到划分的根节点
for(rootIndex=prel+1; rootIndex<= prer; rootIndex++){
if(pre[rootIndex] == post[postr-1]) break;
}
if(rootIndex-prel > 1){ // 判断子树是否唯一的标准????
getIn(prel+1, rootIndex-1, postl, postl+rootIndex-1-prel-1); // 左
}else{
judge = false;
}
in.push_back(post[postr]); // 中
getIn(rootIndex, prer, postl+rootIndex-1-prel, postr-1); // 右
}
}
int main(){
int N;
scanf("%d",&N);
pre.resize(N), post.resize(N);
for(int i=0; i<N; i++) scanf("%d", &pre[i]);
for(int i=0; i<N; i++) scanf("%d", &post[i]);
getIn(0, N-1, 0, N-1);
printf("%s\n%d", judge?"Yes":"No", in[0]);
for(int i=1; i<in.size(); i++){
printf(" %d",in[i]);
}
printf("\n");
return 0;
}
【1110】判断是否是满二叉树
方法·1:
遍历二叉树,将节点按照index数组have_node中,若数组第一个节点到最后一个节点之间中有空,则不是完全而插入;或者是,最后一个节点的index - 第一个节点的index 大于节点数。
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<cmath>
using namespace std;
struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
};
int N; // number of TreeNode
vector<TreeNode*> node_lst;
vector<int> not_Root = {0};
int have_node[30]; // 遍历二叉树,将节点按照index数组中,若数组第一个节点到最后一个节点之间中有空,则不是完全而插入
int maxn = -1, final_node;
void dfs(TreeNode* root, int index){
if(index>maxn){
maxn = index;
final_node = root->val;
}
if(root->left) dfs(root->left, index*2);
if(root->right) dfs(root->right, index*2+1);
}
void judge_have_node(TreeNode* root, int index){
have_node[index] = root->val;
if(root->left) judge_have_node(root->left, index*2);
if(root->right) judge_have_node(root->right, index*2+1);
return;
}
int main(){
// 根据输入构造二叉树
cin>>N;
not_Root.resize(N);
node_lst.resize(N);
for(int i=0; i<N; i++)
node_lst[i] = new TreeNode();
for(int i=0; i<N; i++){
string a, b;
cin>>a>>b;
if(a=="-"){
node_lst[i]->left = NULL;
}else{
int x = stoi(a);
node_lst[i]->left = node_lst[x];
not_Root[x] = 1;
}
if(b=="-"){
node_lst[i]->right = NULL;
}else{
int y = stoi(b);
node_lst[i]->right = node_lst[y];
not_Root[y] = 1;
}
node_lst[i]->val = i;
}
// 找到根节点
int root_index = 0;
for(int i=0; i<N; i++){
if(!not_Root[i]) root_index = i;
}
TreeNode* root = node_lst[root_index];
// 方法1:依次遍历二叉树的节点,将其存放到对应的数组的位置,若数组有空(最后一个节点与第一个节点之间的距离大于节点数目),则不是满二叉树
fill(have_node, have_node+30, -1);
judge_have_node(root, 1);
int i;
for(i=29; i>=0; i--){
if(have_node[i]!=-1) break;
}
if(i>N){
cout<<"NO "<<root->val<<endl;
}else{
cout<<"YES "<<have_node[i]<<endl;
}
/*
// 方法2: 深度遍历,也是根据index下标判断
dfs(root, 1); // 深度遍历,按照满二叉树的方式遍历到最后一个节点,得到最后一个节点对应的index和值final_node;
// 由于是按照满二叉树的方式遍历,计算的最大index值maxn应该等于节点总数N;若不等,则说明不是满二叉树
if(maxn > N){
cout<<"NO "<<root->val<<endl;
}else{
cout<<"YES "<<final_node<<endl;
}
// 层序遍历:计算每层的节点数目,目前有bug
// 得分21分,有两个点过不去
// 层次遍历,判断每层的节点数是否是2^h-1(h为深度height)
queue<TreeNode*> que;
int height = 0,pop_num = 0;
bool is_complete = true;
TreeNode* temp = root;
if(root!=NULL)
que.push(root);
else is_complete = false;
while(! que.empty()){
int size = que.size();
for(int j=0; j<size; j++){
temp = que.front();
que.pop();
if(temp->left) que.push(temp->left);
if(temp->right) que.push(temp->right);
}
height += 1;
pop_num += size;
if(size != pow(2, height-1) && pop_num!=N){
is_complete = false;
break;
}
}
printf("%s ", is_complete?"YES":"NO");
printf("%d\n", is_complete?temp->val: root->val);
*/
return 0;
}
【1102】翻转二叉树,dfs,树的遍历,递归层序遍历
中序遍历依旧利用dfs,深度优先遍历;
层序遍历也可用递归,在TreeNode声明中加入index和level,index为下标,level为深度,可现深度升序,深度相同index升序进行层序输出;
注意翻转那一步可以在输入时候完成,存储时直接将左右节点存在相反的位置。
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
struct TreeNode{
int val, index, level; // val数值,index下标,level深度(层数)
TreeNode* left;
TreeNode* right;
};
int n;
vector<TreeNode*> Tree;
vector<TreeNode> level_order, in_order;
vector<bool> not_root;
bool cmp(TreeNode a, TreeNode b){
if(a.level == b.level) return a.index<b.index;
else return a.level < b.level;
}
void invert(TreeNode* root){
if(root==NULL) return;
swap(root->left, root->right);
invert(root->left);
invert(root->right);
}
void dfs(TreeNode* root , int index, int level){
if (root==NULL) return;
// 前
dfs(root->left, index*2, level+1);
// 中
root->index = index;
root->level = level;
in_order.push_back(*root);
// 右
dfs(root->right, index*2+1, level+1);
}
int main(){
// 输入构建二叉树
cin>>n;
Tree.resize(n);
not_root.resize(n);
for(int i=0; i<n; i++)
Tree[i] = new TreeNode();
for(int i=0; i<n; i++){
string sx, sy;
cin>>sx>>sy;
if(sx=="-"){
Tree[i]->left = NULL;
}else{
Tree[i]->left = Tree[stoi(sx)];
not_root[stoi(sx)] = true;
}
if(sy=="-"){
Tree[i]->right = NULL;
}else{
Tree[i]->right = Tree[stoi(sy)];
not_root[stoi(sy)] = true;
}
Tree[i]->val = i;
}
// 找到根节点的下标
int root_index = 0;
while(not_root[root_index]) root_index++;
TreeNode* root = Tree[root_index];
// 翻转二叉树
invert(root);
// 深度优先遍历
dfs(root, 1, 1);
// 层序遍历
level_order = in_order;
sort(level_order.begin(), level_order.end(), cmp); // 重新排序,如果level相等按照index从小到大,否则按照level从小到大
for(int i=0; i<n; i++){
printf("%d%s", level_order[i].val, i==n-1?"\n":" ");
}
// 输出中序in-order遍历的数值
for(int i=0; i<n; i++){
printf("%d%s", in_order[i].val, i==n-1?"\n":" ");
}
return 0;
}
【1106】dfs+树的遍历
题⽬⼤意:提供⼀棵树,在树根处货物的价格为p,从根结点开始每往下⼀层,该层货物价格将会在⽗
亲结点的价格上增加r%。求叶⼦结点出能获得的最低价格以及能提供最低价格的叶⼦结点数
#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
struct node{
bool is_retailer = false;
vector<int> child;
}ID[100000];
int N;
double P, r;
int temp_depth, path_num = 0, lowest_depth = 100000;
// 深度遍历
void dfs(int root, int temp_depth){
/*
如果这个节点是retailer(叶子节点)
如果当前的temp_depth小于最小的深度,则更新最小深度,并置最短路径数目为1
如果当前temp_depth等于最小深度,则这是另一条最短路径,路径数目+1
否则,遍历这个节点的孩子,回溯隐藏在temp_depth+1中
*/
if(ID[root].is_retailer){
if(temp_depth<lowest_depth){
lowest_depth = temp_depth;
path_num = 1;
}else if(temp_depth == lowest_depth){
path_num += 1;
}
return;
}else{
for(int i=0; i<ID[root].child.size(); i++){
dfs(ID[root].child[i], temp_depth+1);
}
}
}
int main(){
/*
(1)输入数据
*/
cin>>N>>P>>r;
int k;
for(int i=0; i<N; i++){
scanf("%d", &k);
int c;
if(k==0) ID[i].is_retailer = true;
for(int j=0; j<k; j++){
scanf("%d", &c);
ID[i].child.push_back(c);
}
}
/*
深度遍历,root=0,初始化temp_depth=0
*/
dfs(0,0);
/*
更新price,输出
*/
for(int i=0; i<lowest_depth; i++)
P *= (1+0.01*r);
printf("%.4f %d\n",P, path_num);
// printf("%.4f %d", P * pow(1 + r/100, lowest_depth), path_num);
return 0;
}
【1130】迭代中序遍历,输出字符串、dfs
#include<iostream>
#include<string>
using namespace std;
struct node{
string val;
int left, right;
}data[21];
int N;
string s = "";
void in_order(int index){
if(data[index].left==-1 && data[index].right==-1){
s += data[index].val;
return;
}
s += "(";
if(data[index].left != -1) in_order(data[index].left);
s += data[index].val;
if(data[index].right != -1) in_order(data[index].right);
s += ")";
}
string dfs(int i){
if(data[i].left==-1 && data[i].right==-1)
return data[i].val;
else if(data[i].left==-1 && data[i].right!=-1)
return "("+data[i].val+dfs(data[i].right)+")";
else if(data[i].left!=-1 && data[i].right!=-1)
return "("+dfs(data[i].left)+data[i].val+dfs(data[i].right)+")";
}
int main(){
// 输入
cin>>N;
int not_root[100] = {0}, root=1;
for(int i=1; i<=N; i++){ //注意节点下标从1开始到N结束
cin>>data[i].val>>data[i].left>>data[i].right;
if(data[i].left!=-1) not_root[data[i].left] = 1;
if(data[i].right!=-1) not_root[data[i].right] = 1;
}
// 找到根节点
while(not_root[root] == 1) root++;
// 中序遍历
in_order(root);
if(s[0]=='(') s = s.substr(1, s.size()-2); //如果不设置这步,则会增加一对括号
cout<<s<<endl;
// string result = dfs(root);
// if(result[0]=='(') result = result.substr(1, result.size()-2);
// cout<<result;
return 0;
}
【1138】前序中序直接转后序,无需构造树
根据前序和中序遍历的节点,直接输出后序;
即通过前序和中序找根节点,先遍历左子树,再遍历右子树,最后将根节点的值加入path中
#include<iostream>
#include<vector>
using namespace std;
vector<int> pre_order, in_order, path;
// 前序中序遍历,直接转后序遍历(左右中)
void post_order(int pre_l, int in_l, int in_r){
if(in_l>in_r) return;
int i = in_l; // 找in_order中根节点的下标
while(in_order[i]!=pre_order[pre_l]) i++;
post_order(pre_l+1, in_l, i-1); // left
post_order(pre_l+1+i-in_l, i+1, in_r); // right
path.push_back(in_order[i]); // middle
}
int main(){
int N;
cin>>N;
pre_order.resize(N);
in_order.resize(N);
for(int i=0; i<N; i++) scanf("%d", &pre_order[i]);
for(int i=0; i<N; i++) scanf("%d", &in_order[i]);
post_order(0, 0, N-1);
cout<<path[0]<<endl;
// for(int i=0; i<N; i++) cout<<path[i]<<endl;
return 0;
}
【1143】二叉搜索树,左节点小于该节点,右节点大于等于该节点
1143 Lowest Common Ancestor (30 分)
#include<iostream>
#include<vector>
#include<map>
using namespace std;
/*
二叉搜索树给出的无所谓是什么遍历,它最终一定是按照顺序排列的;
如果一个节点a,u<=a && v>=a 或者 v<=a && u>=a 则a一定是两者的共同祖先
因此对于每一对u和v,遍历一遍二叉搜索树中所有的节点,判断是否有满足上述条件的节点a就行。
用mp来标注某个数值的节点是否被输入,如果没有则not found。
*/
int main(){
int n ,m;
cin>>m>>n;
int a, u, v, tree[n];
map<int,bool> mp;
for(int i=0; i<n; i++){
scanf("%d", &tree[i]);
mp[tree[i]] = true;
}
while(m--){
scanf("%d %d", &u, &v);
for(int j=0; j<n; j++){
a = tree[j];
if((u<=a && v>=a) || (u>=a && v<=a)) break;
}
if (mp[u] == false && mp[v] == false)
printf("ERROR: %d and %d are not found.\n", u, v);
else if (mp[u] == false || mp[v] == false)
printf("ERROR: %d is not found.\n", mp[u] == false ? u : v);
else if (a == u || a == v)
printf("%d is an ancestor of %d.\n", a, a == u ? v : u);
else
printf("LCA of %d and %d is %d.\n", u, v, a);
}
return 0;
}
【1099】已知二叉搜索树形状,插入数值,其中序遍历就是数值的升序排列
1099 Build A Binary Search Tree (30 分)
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
using namespace std;
struct node{
int val;
int l=-1, r=-1;
}tree[101];
vector<int> pre;
void in_order(int index){ //中序遍历
if(tree[index].l!=-1) in_order(tree[index].l);
pre.push_back(index);
if(tree[index].r!=-1)in_order(tree[index].r);
}
int main(){
int n;
cin>>n;
// 输入左右子节点
for(int i=0; i<n; i++) scanf("%d %d", &tree[i].l, &tree[i].r);
// 二叉搜索树中序遍历就是升序
in_order(0);
// 输入数值,并升序排序
vector<int> number(n);
for(int i=0; i<n; i++)
scanf("%d", &number[i]);
sort(number.begin(), number.end());
// 中序遍历的序列对应的tree[pre[i]]的值为number[i]
for(int i=0; i<n; i++){
tree[pre[i]].val = number[i];
}
// 层序遍历,输出结果
queue<node> que;
que.push(tree[0]);
cout<<tree[0].val;
int num = 0;
while(!que.empty()){
node t = que.front();
que.pop();
if(num++>0) cout<<" "<<t.val;
if(t.l!=-1) que.push(tree[t.l]);
if(t.r!=-1) que.push(tree[t.r]);
}
return 0;
}
【1094】dfs 树的遍历计算每层节点的数目(🌳)
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
vector<int> v[100];
int book[100];
void dfs(int index, int level){ // 计算每一层节点的数目
book[level]++;
for(int i=0; i<v[index].size(); i++){
dfs(v[index][i], level+1);
}
}
int main(){
int N, M, ID, c, k;
cin>>N>>M;
for(int i=0; i<M; i++){
scanf("%d %d", &ID, &k);
for(int j=0; j<k; j++){
scanf("%d", &c);
v[ID].push_back(c);
}
}
dfs(1,1); // 根节点是01,第一层
int max_num = -1, target_level = -1;
for(int i=0; i<100; i++){
if(book[i]>max_num){
max_num = book[i];
target_level = i;
}
}
printf("%d %d\n", max_num, target_level);
}
/*
struct node{
vector<int> children;
}f[100];
int main(){
int N, M, ID, c, k;
cin>>N>>M;
for(int i=0; i<M; i++){
scanf("%d %d", &ID, &k);
for(int j=0; j<k; j++){
scanf("%d", &c);
f[ID].children.push_back(c);
}
}
queue<node> que;
que.push(f[1]);
int max_num = -1, depth = 0, target_depth = 0;
while(!que.empty()){
depth++;
int qs = que.size();
if(qs > max_num){
max_num = qs;
target_depth = depth;
}
for(int i=0; i<qs; i++){
node temp = que.front();
que.pop();
for(int j=0; j<temp.children.size(); j++){
que.push(f[temp.children[j]]);
}
}
}
printf("%d %d\n", max_num, target_depth);
}
*/