二叉树的遍历
- 先序遍历
- 中序遍历
- 后序遍历
- 层序遍历
经典问题1:给出两个遍历树的序列,如何复现树?🎄
答:一定会需要中序遍历来区分左右子树,中序遍历+(剩余3种任一遍历的数组即可复现原来的树的结构)
练习1
这题是根据中序和后序来复现树的结构,再用层次遍历输出即可,思路不是特别难,主要涉及复现函数create以及层次遍历函数levelTravel
复现函数《算法笔记》给了先序+中序的版本,可以参考画图加深理解
//1020
#include<bits/stdc++.h>
using namespace std;
int n;
int pos[100];
int in[100];
//定义树的结构体
struct node{
int data;
node*lchild;
node*rchild;
};
//给出后序遍历+中序遍历->层序遍历
void levelTravel(node*root){
int count=0;
//层序遍历
queue<node*>q;
node*temp=new node;
q.push(root);
while(!q.empty()){
count++;
temp=q.front();
printf("%d",temp->data);//这里需要注意输出格式
if(count<n)printf(" ");
q.pop();
if(temp->lchild!=NULL){
q.push(temp->lchild);
}
if(temp->rchild!=NULL){
q.push(temp->rchild);
}
}
return ;
}
node*create(int posL,int posR,int inL,int inR){
//递归:复现树
if(posL>posR){
return NULL;//递归边界
}
//递归
node*root=new node;
//根节点是后序遍历最后一个元素
root->data=pos[posR];
int k;
for(k=inL;k<=inR;k++){
if(root->data==in[k]){
break;
}
}
//中序,左子树元素个数
int left_num=k-inL;
//debug
//printf("中序:%d个左子树\n",left_num);
root->lchild=create(posL,posL+left_num-1,inL,k-1);
root->rchild=create(posL+left_num,posR-1,k+1,inR);
return root;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&pos[i]);
}
for(int i=1;i<=n;i++){
scanf("%d",&in[i]);
}
//根据后序遍历+中序遍历求树
node*root=create(1,n,1,n);
//debug
//printf("root=%d\n",root->data);
//中序遍历
levelTravel(root);
return 0;
}
练习2
PTA甲级1086
这题的思路在于,列出题干中的输入顺序:
- push依次输入的顺序,单独看就是先序遍历树的结果
- pop出栈的顺序,也就是自己写一个栈,按照题目输入输出操作一波得到的数列,就是中序遍历的结果
- 我们已经知道中序+任一其他遍历结果即可复现原来的树(这也是本题为什么会想到上两个点的突破口)
代码不难,和上一题几乎一样,只不过多了栈的处理以及输入的对于先序、中序数列的存储问题😊
//1086
#include<bits/stdc++.h>
using namespace std;
int pre[100];
int in[100];
struct node{
int data;
node*rchild;
node*lchild;
};
//解题思路:
//push 的顺序是先序
//pop的顺序是中序
//给出后序遍历
node*create(int preL,int preR,int inL,int inR){
if(preL>preR)return NULL;
node*root=new node;
root->data=pre[preL];
int k;
for(k=inL;k<=inR;k++){
if(in[k]==root->data)
break;
}
int num_left=k-inL;
root->lchild=create(preL+1,preL+num_left,inL,k-1);
root->rchild=create(preL+num_left+1,preR,k+1,inR);
return root;
}
int flag=0;
int n;
void posTravel(node*root){
if(root==NULL){
return;
}
posTravel(root->lchild);
posTravel(root->rchild);
if(flag==0){
printf("%d",root->data);
flag=1;}
else{
printf(" %d",root->data);
}
}
int main(){
stack<int>st;
int data;
string op;
int countpre=0;
int countin=0;
scanf("%d",&n);
while(!(countpre==n&&countin==n)){
cin>>op;
if(op=="Push"){
cin>>data;
pre[++countpre]=data;
st.push(data);
}else{
data=st.top();
st.pop();
in[++countin]=data;
}
}
/*debug
for(int i=1;i<=n;i++){
printf("%d ",pre[i]);
}
printf("\n");
for(int i=1;i<=n;i++){
printf("%d ",in[i]);
} */
node*root=create(1,n,1,n);
posTravel(root);
return 0;
}
练习3
PTA甲级1102
这题考的是:
- 静态二叉树的定义
- 静态二叉树的层次遍历以及中序遍历(虽然不太清楚为啥遍历输出的时候,左右位置得颠倒~昂)
难点我觉得是如何寻找根节点,因为题目N的最大值为10吗,所以我用中序遍历遍历每一个结点,哪个能遍历完整个树,哪个结点就是根节点,当然这样写在题目大的时候肯定超时啦~
下面是AC代码:
今天三题目都是一次AC的,说明上学期数据结构理论课我学的不错~
//1102
#include<bits/stdc++.h>
using namespace std;
//考察的是静态树
struct Tree{
int data;
int rchild=-1;
int lchild=-1;
}node[100];
int ccount;
//寻找根节点
void rootfind(int root){
if(root==-1)return ;
rootfind(node[root].lchild);
ccount++;
rootfind(node[root].rchild);
return ;
}
bool flag=false;
//中序遍历
void inTravel(int root){
if(root==-1)return ;
inTravel(node[root].rchild);
if(flag==false){
flag=true;
printf("%d",node[root].data);
}else
printf(" %d",node[root].data);
inTravel(node[root].lchild);
return ;
}
//层序遍历
void levelTravel(int root) {
queue<int>q;
q.push(root);
int temp;
while(!q.empty()){
temp=q.front();
if(flag==false){
flag=true;
printf("%d",node[temp].data);
}else{
printf(" %d",node[temp].data);
}
q.pop();
if(node[temp].rchild!=-1)q.push(node[temp].rchild );
if(node[temp].lchild!=-1)q.push(node[temp].lchild);
}
}
int main(){
int n;
scanf("%d",&n);
string ch1,ch2;
int r,l;
for(int i=0;i<n;i++){
cin>>ch1>>ch2;
if(ch1!="-"){
l=stoi(ch1);
node[i].lchild=l;
}
if(ch2!="-"){
r=stoi(ch2);
node[i].rchild=r;
}
node[i].data=i;
}
//如何找到根节点
int root=-1;
for(int i=0;i<n;i++){
ccount=0;
rootfind(i);
if(ccount==n){
root=i;
break;
}
}
levelTravel(root);
printf("\n");
flag=false;
inTravel(root);
return 0;
}
树的遍历
结构体中,子节点用vector实现以便节省空间,本书中树的遍历利用的是静态的写法
练习1:树的建立(给出结点孩子)以及层次遍历
PTA甲级1079
这题考察的是静态树的层次遍历
傻了傻了,阅读理解的问题,卡在p+r还是p+r%这个问题上半天~完全做题做傻了😂
//1079,1090
#include<bits/stdc++.h>
using namespace std;
int n;
double p,r;
const int maxn=200000;
struct Node{
double price;
int amount=0;
vector<int>child;
}node[maxn];
bool isc[maxn];
//层序遍历,解决每一层的价值:难点,如何找到根节点
double sum=0;
void levelTravel(int root){
queue<int>q;
q.push(root);
while(!q.empty()){
int top=q.front();
q.pop();
for(int i=0;i<node[top].child.size();i++){
//遍历子节点,将子节点加入队列中
int child=node[top].child[i] ;
q.push(child);
node[child].price=node[top].price*(1+r/100);
}
if(node[top].child.size()==0){
//叶子结点
//printf("叶子: p=%.2f amount=%d\n",node[top].price,node[top].amount);
sum+=(node[top].price*node[top].amount);
}
}
return;
}
int main(){
memset(isc,false,sizeof(isc));
scanf("%d %lf %lf",&n,&p,&r);
int num;
int temp;
for(int i=0;i<n;i++){
scanf("%d",&num);
if(num!=0){
for(int j=0;j<num;j++){
scanf("%d",&temp);
node[i].child.push_back(temp);//保存子节点的位置
isc[temp]=true;
}
}
else{
scanf("%d",&node[i].amount);
}
}
int root=-1;
for(int i=0;i<n;i++){
if(!isc[i]){
root=i;
break;
}
}
//printf("root=%d\n",root);
node[root].price=p;
levelTravel(root);
printf("%.1f",sum);
return 0;
}
练习2:树的建立(给出父节点)以及层次遍历
这题和上一题思路大致相同,不是很难~😊主要的区别在于:
- 建树的过程:本题是给出每个结点的父节点,所以输入的时候处理方法和上一题不同
- 层次遍历中间的处理:为了得到最大值以及最大值个数,需要在每次从队列里pop的时候记录更新最大值
回顾vector以及queue的很好的题目,以及这类树的题目建议在草稿纸上作图,辅助理解,效果更佳了😛
//1090
#include<bits/stdc++.h>
using namespace std;
int n;
double p,r;
struct Node{
double price;
vector<int>child;
}node[200000];
//记录最大值:maxp
//记录最大值个数:max_num
double maxp=0;
int max_num=0;
void levelTravel(int root){
queue<int>q;
q.push(root);
int top;
while(!q.empty()){
top=q.front();
//加这一步是为了更新答案
if(node[top].price>maxp){
maxp=node[top].price;
max_num=1;
}else if(node[top].price==maxp){
max_num++;
}
q.pop();
for(int i=0;i<node[top].child.size();i++){
int c=node[top].child[i];
q.push(c);
node[c].price=node[top].price*(1+r/100);
}
}
return;
}
int main(){
scanf("%d %lf %lf",&n,&p,&r);
int root=-1;
int father;
//根据输入建立树结构
for(int i=0;i<n;i++){
scanf("%d",&father);
if(father==-1){
root=i;
continue;
}
node[father].child.push_back(i);
}
//初始化根节点的价格
node[root].price=p;
//层次遍历,搜索答案
levelTravel(root);
printf("%.2f %d",maxp,max_num);
return 0;
}
练习3:层次遍历求层数以及求出结点数最大层的层号
这题的突破口其实和前两题一样,主要改动还是在层次遍历的pop()那边,可以记作一类模板题🐵:
- 用数组num_g存储每一层人数,改进的话可以改一下数组的大小,我这边其实开大了,但是不影响解题目
- 遍历该数组输出最大值以及对应序号(层号)
//1094
#include<bits/stdc++.h>
using namespace std;
const int maxn=200;
struct Node{
int layer;
vector<int>child;
}node[maxn];
bool isc[maxn];
int m,n,num,id,temp;
int num_g[maxn]={0};
//计算最大的一代,以及对应人数
void levelTravel(int root){
queue<int>q;
q.push(root);
while(!q.empty()){
int top=q.front();
num_g[node[top].layer]++;//每代的人数计算
q.pop();
for(int i=0;i<node[top].child.size();i++){
int c=node[top].child[i];
node[c].layer=node[top].layer+1;
q.push(c);
}
}
}
int main(){
memset(isc,false,sizeof(isc));
scanf("%d %d",&n,&m);
for(int i=0;i<m;i++){
scanf("%d %d",&id,&num);
for(int j=0;j<num;j++){
scanf("%d",&temp);
isc[temp]=true;
node[id].child.push_back(temp);
}
}
//找到根节点
int root=1;
//debug
//printf("根节点为:%d\n",root);
node[root].layer=1;
levelTravel(root);
int max_g=0,max_num=0;
for(int i=1;i<=n;i++){
if(num_g[i]>max_num){
max_num=num_g[i];
max_g=i;
}
}
printf("%d %d",max_num,max_g);
return 0;
}
练习4:层次遍历,浮点数的判断大小(细节,但是真的很坑)
这题论思路,和前两题一样一样的,主要需要解决浮点数double类型不可以直接用==判断是否相等的问题:
- 其实我没有解决这个问题,而是给每个节点加了一个layer的数据域,因为由树的性质可知,同一层结点的price必定相等,只需要记录叶子结点的最小层数即可
- 我看到有直接用浮点数判断等于做的,应该是先*10000再比较叭,具体我也没有再仔细推敲,源码里面有一处isEqual()判断两浮点数相等的写法,借鉴了这篇不过最后AC的版本,那个函数是闲置函数~😅
//1106
#include<bits/stdc++.h>
#define Epslion 1e-2 //定义极小数
using namespace std;
const int maxn=200000;
double p,r;
struct Node{
double price=p;
int layer;
vector<int>child;
}node[maxn];
int n;
bool isc[maxn];
//浮点数字判断相等
bool isEqual(double a ,double b){
if (abs(a-b)<Epslion)return true;
return false;
}
//层次遍历,找叶子节点里面,price最小的
double min_p=0;int min_num=0;
int min_layer=0;
void levelTravel(int root){
queue<int>q;
q.push(root);
while(!q.empty()){
int top=q.front();
// printf("价钱:%lf\n",node[top].price);
q.pop();
//关于解的更新问题
if(node[top].child.size()==0){
// printf("叶子\n");
if(min_p==0){//解没有更新的情况
//min_p=node[top].price;
min_p=node[top].price;
min_num=1;
min_layer=node[top].layer;
//printf("123min_p=%lf\n",min_p);
//printf("1叶子\n");
}else if(node[top].layer==min_layer){//如果同样的话,则个数+1
min_num++;
//printf("2叶子\n");
}else if(node[top].price<min_p){//如果比现有的小的话,则更新答案
min_p=node[top].price;
min_num=1;
min_layer=node[top].layer;
//printf("3叶子\n");
}
//printf("min_p=%lf\n",min_p);
}
for(int i=0;i<node[top].child.size();i++){
int c=node[top].child[i];
node[c].layer=node[top].layer+1;
node[c].price=node[top].price*(1+r/100);
q.push(c);
}
}
}
int main(){
memset(isc,false,sizeof(isc));
scanf("%d %lf %lf",&n,&p,&r);
int num;int id;
for(int i=0;i<n;i++){
scanf("%d",&num);
for(int j=0;j<num;j++){
scanf("%d",&id);
isc[id]=true;//id是孩子结点
node[i].child.push_back(id);
}
}
int root=-1;
for(int i=0;i<n;i++){
if(!isc[i]){
root=i;
break;
}
}
//根节点
//printf("root=%d\n",root) ;
node[root].price=p;
levelTravel(root);
printf("%.4f %d",min_p,min_num);
return 0;
}
练习5:层次遍历,构建树
PTA甲级1004
这题感觉不值30,比上一题要简单,也没有坑😀
具体思路参见1、2
这里多加了一个存储答案的数组
//1004
#include<bits/stdc++.h>
using namespace std;
int n,m;
//计算每一层的个数
const int maxn=400;
struct Node{
int layer;
vector<int>child;
}node[maxn];
int ans[maxn]={0};
bool isc[maxn]={false};
int max_layer=0;
//层次遍历
void levelTravel(int root){
queue<int>q;
q.push(root);
while(!q.empty()){
int top=q.front();
if(node[top].child.size()==0){
ans[node[top].layer]++;
if(node[top].layer>max_layer)max_layer=node[top].layer;
}
q.pop();
for(int i=0;i<node[top].child.size();i++){
int c=node[top].child[i];
node[c].layer=node[top].layer+1;
q.push(c);
}
}
}
int main(){
scanf("%d%d",&n,&m);
int id,num,temp;
for(int i=0;i<m;i++){
scanf("%d%d",&id,&num);
for(int j=0;j<num;j++){
scanf("%d",&temp);
node[id].child.push_back(temp);
isc[temp]=true;
}
}
int root=1;
for(int i=1;i<=n;i++){
if(!isc[i]){
root=i;
break;
}
}
//debug
node[root].layer=1;
//printf("root=%d\n",root);
levelTravel(root);
bool flag=false;
for(int i=1;i<=max_layer;i++){
if(!flag){
printf("%d",ans[i]);
flag=true;
}else{
printf(" %d",ans[i]);
}
}
return 0;
}
练习6:
这题整了1h+,应该算是树的目前写到的题目里面最耗时的一题了 从😜
思路:
- 在树的结构体中建立sum来记录从根节点到当前结点的权重之和,vector类型father来存储路径上的结点权重(其实这里命名为father不妥帖,因为其实也包含自身节点的权重信息,命名改成path可能代码阅读性会更好)
- 上述思路产生的原因是,在树的结构中,从根到某一结点的路径有且只有一条,所以没有路径覆盖的情况,和图不一样嗷🛒
- 个人感觉的难点是最后输出的排序,一开始想在child结点进入队列前排序,后来发现这样解决不了层数不同的排序问题,于是老老实实用二维的vector也就是ans来储存答案,最后排序输出,AC完结撒花~💖
//1053
#include<bits/stdc++.h>
using namespace std;
//感觉像是广搜
const int maxn=200;
//从根节点到某一个结点的路径有且只有一个
struct Node{
int w;
int sum;
vector<int>child;
vector<int>father;//保存从根节点到当前结点的路径
}node[maxn];
int n,m,s;
vector<vector<int> >ans;
bool cmp(vector<int>a,vector<int>b){
int i;
for(i=0;i<a.size()&&i<b.size();i++){
if(a[i]!=b[i]){
return a[i]>b[i];
}
}
if(i<a.size()||i<b.size()){
return a.size()>b.size();
}
}
//层次遍历求出每一结点的sum
void levelTravel(int root){
queue<int>q;
q.push(root);
while(!q.empty()){
int top=q.front();
//debug
//printf("当前权重之和:%d\n",node[top].sum) ;
if(node[top].sum==s&&node[top].child.size()==0){
ans.push_back(node[top].father);//存储答案
}
q.pop();
//考虑剪枝
//考虑将孩子结点按照各自权重排序 --->因为层数不同,所以此法不行
//sort(node[top].child.begin(),node[top].child.end(),cmp);
for(int i=0;i<node[top].child.size();i++){
int c=node[top].child[i];
node[c].sum=node[c].w+node[top].sum;
for(int j=0;j<node[top].father.size();j++){
node[c].father.push_back(node[top].father[j]);
}
node[c].father.push_back(node[c].w);
q.push(c);
}
}
}
int main(){
scanf("%d %d %d",&n,&m,&s);
for(int i=0;i<n;i++){
scanf("%d",&node[i].w);
}
int id,num,temp;
for(int i=0;i<m;i++){
scanf("%d %d",&id,&num);
for(int j=0;j<num;j++){
scanf("%d",&temp);
node[id].child.push_back(temp);//存储孩子结点
}
}
int root=0;//root在本题默认为0
node[root].sum=node[root].w;
node[root].father.push_back(node[root].w);
levelTravel(root);
//将答案排序
sort(ans.begin(),ans.end(),cmp);
for(int i=0;i<ans.size();i++){
for(int j=0;j<ans[i].size();j++){
if(j==0)printf("%d",ans[i][j]);
else{
printf(" %d",ans[i][j]);
}
}
if(i!=ans.size()-1)
printf("\n");
}
return 0;
}
二叉排序树BST
练习1:考察先序遍历+中序遍历->建立树,以及后序遍历输出
注意点,这题一开始我想用静态树结构,但是会出现死循环的情况,原因是输入的树的结点值可以相同(还有负数情况),综上所以改用指针结构:
- 由于排序树的性质,题目隐含条件是中序遍历结果为输入数组的增序/降序,代码函数isBTS根据建树的函数改编如下
if(preL>preR){//建立结束
flag=true;
return NULL;
}- 需要注意的是,isRBTS虽然相似,但是在找中序k位置的时候,i要从后往前遍历,否则后序遍历的结果有误
今日份编程完结🤣
//1043
#include<bits/stdc++.h>
using namespace std;
const int maxn=2000;
bool is_first=true;
int pre[maxn];
int in[maxn];
int inr[maxn];
struct node{
int data;
node*lchild=NULL;
node*rchild=NULL;
};
bool cmp(int a,int b){
return a>b;
}
bool flag=false;
//判断是否可以建成树
node* isBST(int preL,int preR,int inL,int inR){
if(preL>preR){//建立结束
flag=true;
return NULL;
}
node*root=new node;
root->data=pre[preL];
int k=-1;
for(int i=inL;i<=inR;i++){
if(in[i]==root->data){
k=i;
break;
}
}
if(k==-1){//如果找不到,就是不符合
flag=false;
return NULL;
}
//左边的个数
int left_num=k-inL;
root->lchild=isBST(preL+1,preL+left_num,inL,k-1);
root->rchild=isBST(preL+left_num+1,preR,k+1,inR);
return root;
}
node* isRBST(int preL,int preR,int inL,int inR){
if(preL>preR){//建立结束
flag=true;
return NULL;
}
node*root=new node;
root->data=pre[preL];
int k=-1;//root在in[]的位置
for(int i=inR;i>=inL;i--){
if(inr[i]==root->data){
k=i;
break;
}
}
if(k==-1){//如果找不到,就是不符合
flag=false;
return NULL;
}
//左边的个数
int left_num=k-inL;
root->lchild=isRBST(preL+1,preL+left_num,inL,k-1);
root->rchild=isRBST(preL+left_num+1,preR,k+1,inR);
return root;
}
//后序遍历
void posTravel(node* root){
if(root==NULL){
return;
}
posTravel(root->lchild);
posTravel(root->rchild);
if(is_first){
printf("%d",root->data);
is_first=false;
}
else printf(" %d",root->data);
}
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&pre[i]);
in[i]=pre[i];inr[i]=pre[i];
}
sort(in,in+n);//中序遍历是从小到大
sort(inr,inr+n,cmp);
//根据先序遍历的结果判断是否是二叉排序树 ,是的话输出后续遍历结果
node*root=isBST(0,n-1,0,n-1);
if(flag){
printf("YES\n");
posTravel(root);
}else {
root=isRBST(0,n-1,0,n-1);
if(flag){
printf("YES\n");
posTravel(root);
}else{
printf("NO");
}
}
return 0;
}
//应该用带链表的树
练习2:完全二叉树和BST混合在一起
题解:
感觉是一道比较难的题目,最开始的时候真的无从下手,整理一下思路,有两版(第一版是思路雏形,但是卡在一个测试点,第二版OK)
- 对于完全二叉树为什么可以唯一确定一BST,因为它固定了树的形状,这一点证明大家可以自行画图
- 所以思路就是先用层序遍历建立完全二叉树的形状,在中序遍历利用BST 的性质构建树
- 最后层序遍历建好的BST,输出就好
- 注意的是用指针结构会内存超限
第二版参考了知乎的一篇文章
觉得自己有被大佬们过于简洁的代码冒犯到,嘤嘤嘤😶
这位是更好的题解
这题能帮助吃透完全二叉树的数组形式与中序遍历的关系。
晴神书上虽然没讲,但是浙大的数据结构课上提到过,完全二叉树的数组形式,左右子节点分别是根节点的2root+1和2root+2。利用这一关系,可以只通过数组序号实现遍历。
同时,我们要知道二叉树的中序遍历顺序就是有序的(即从小到大)。中序遍历第一个访问的就是最小的节点,它的本质就是不断地向左子树递归。那么,我们结合完全二叉树的数组形式和中序遍历,可以获得首个所访问节点的下标index,将树节点Node的有序数组的首个节点Trees[0]赋给这个下标对应的某个数组空间——假定它是Level,就实现了在中序遍历中将Trees映射到对应的Level从而可知层序遍历的顺序
这是第一版27分:第四个测试点内存超限库😥
//1064
#include<bits/stdc++.h>
using namespace std;
int n;
int temp;
vector<int>seq;
struct node{
int data;
node*lchild;
node*rchild;
};
//根据节点个数建立一棵完全二叉树
node*Create(int n){
queue<node*>q;
node*root=new node;
root->lchild=NULL;
root->rchild=NULL;
int count=1;
q.push(root);
while(!q.empty()){
node*top=q.front();
q.pop();
top->lchild=NULL;
top->rchild=NULL;
//左边
node*l=new node;
count++;
top->lchild=l;
l->rchild=NULL;
l->lchild=NULL;
q.push(l);
if(count==n){
while(!q.empty())q.pop();
return root;
}
//右边
node*r=new node;
count++;
top->rchild=r;
r->rchild=NULL;
r->lchild=NULL;
q.push(r);
if(count==n){
while(!q.empty())q.pop();
return root;
}
}
}
int id=0;
//中序遍历该二叉树并填充值
void inTravel(node*root){
if(root==NULL){
return;
}
inTravel(root->lchild);
//赋值:不清楚这样对不对
root->data=seq[id++];
inTravel(root->rchild);
}
bool flag=false;//保证输出格式的
//层序输出
void levelTravel(node*root){
queue<node*>q;
q.push(root);
while(!q.empty()){
node*top=q.front();
if(!flag){
printf("%d",top->data);
flag=true;
} else{
printf(" %d",top->data);
}
q.pop();
if(top->lchild!=NULL){
q.push(top->lchild);
}
if(top->rchild){
q.push(top->rchild);
}
}
return ;
}
int main(){
scanf("%d",&n);
int num;
for(int i=0;i<n;i++){
scanf("%d",&num);
seq.push_back(num);
}
sort(seq.begin(),seq.end());
node*root=Create(n);
inTravel(root);
//输出答案
levelTravel(root);
return 0;
}
第二版:虽然AC 但是不是自己最初想到的方法555~但是这种必须得会吖,不然考场上确实很亏
这个必须会嗷👀
//1064的数组写法
#include<bits/stdc++.h>
using namespace std;
vector<int>seq;
vector<int>level(2000);
int n;
int temp;
int node=0;
void inTravel(int index){
if(index>=n)return ;
inTravel(2*index+1);
level[index]=seq[node++];
inTravel(2*index+2);
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&temp);
seq.push_back(temp);
}
sort(seq.begin(),seq.end());
inTravel(0);
printf("%d",level[0]);
for(int i=1;i<n;i++){
printf(" %d",level[i]);
}
return 0;
}
练习3:BST的性质考察以及层序遍历
题解:
- 发现PTA老喜欢考层序遍历了,所以输出算是形成一种模板了,切记切记
- 这题根据给出的树结构用BST性质填充树,主要利用的是中序遍历输出有序数组的性质,也是上一题(练习2)给我的灵感啦😊
- 还有debug的时候多余输出,记得之后提交要删掉吖
//1099
#include<bits/stdc++.h>
using namespace std;
int seq[200];
bool isc[200]={false};
int n;
struct Node{
int data;
int lchild=-1;
int rchild=-1;
}node[200];
int id=0;
//按照BST的性质填充
void inTravel(int root){
if(root==-1){
return ;
}
inTravel(node[root].lchild);
node[root].data=seq[id++];
inTravel(node[root].rchild);
}
bool flag=false;
//层序遍历
void levelTravel(int root){
queue<int>q;
q.push(root);
while(!q.empty()){
int top=q.front();
if(!flag){
flag=true;
printf("%d",node[top].data);
}else{
printf(" %d",node[top].data);
}
q.pop();
if(node[top].lchild!=-1){
q.push(node[top].lchild);
}
if(node[top].rchild!=-1){
q.push(node[top].rchild);
}
}
}
int main(){
scanf("%d",&n);
int r,l;
for(int i=0;i<n;i++){
scanf("%d %d",&l,&r);
node[i].lchild=l;
node[i].rchild=r;
if(r!=-1)isc[r]=true;
if(l!=-1)isc[l]=true;
}
for(int i=0;i<n;i++){
scanf("%d",&seq[i]);
}
sort(seq,seq+n);
//找到root
int root;
for(int i=0;i<n;i++){
if(!isc[i]){
root=i;
break;
}
}
//debug
//printf("root=%d\n",root);
//填充
inTravel(root);
//输出
levelTravel(root);
return 0;
}
AVL二叉平衡树
这题希望大家回归书本,把二叉平衡树各个子函数的构建好好看一下再默一遍代码,这题我是照着书来的,所以之后肯定得回炉重造,因为默写得很烂,还是翻书🍠才找到BUG在哪里的~
// 1066
#include<bits/stdc++.h>
using namespace std;
vector<int>data;
int n;
struct node{
int data;
int height;
node*lchild;
node*rchild;
};
//获得树高
int getHeight(node*root){
if(root==NULL)return 0;
else return root->height;
}
//更新树高
void update(node*root){
root->height=max(getHeight(root->rchild),getHeight(root->lchild))+1;
}
//得到平衡因子
int getBF(node*root){
return getHeight(root->lchild)-getHeight(root->rchild);
}
//左旋
void L(node* &root){
node*temp=root->rchild;//temp指向B
root->rchild=temp->lchild;
temp->lchild=root;
//更新A、B结点的高度,注意顺序
update(root);
update(temp);
root=temp;
}
//右旋
void R(node* &root){
node*temp=root->lchild;
root->lchild=temp->rchild;
temp->rchild=root;
update(root);
update(temp);
root=temp;
}
//插入结点
void insert(node*&root,int data){
if(root==NULL){//初始根节点为空,插入
node*temp=new node;
temp->data=data;
temp->height=1;
temp->lchild=NULL;
temp->rchild=NULL;
root=temp;
return;
}
if(root->data<data){
insert(root->rchild,data);
update(root);//更新树高
//RR->对root左旋
if(getBF(root)==-2&&getBF(root->rchild)==-1){
L(root);
}
//RL->对root->lchild右旋,root->左旋
if(getBF(root)==-2&&getBF(root->rchild)==1){
R(root->rchild);
L(root);
}
}else if(root->data>data){
insert(root->lchild,data);
update(root);//更新树高
//LL->对root右旋
if(getBF(root)==2&&getBF(root->lchild)==1){
R(root);
}
//LR->对root->lchild左旋,root右旋
if(getBF(root)==2&&getBF(root->lchild)==-1){
L(root->lchild);
R(root);
}
}
}
//建立AVL树,输出根节点
node* createAVL(vector<int>data){
node*root=NULL;//这里需要注意
for(int i=0;i<data.size();i++){
insert(root,data[i]);
//debug
//printf("root=%d\n",root->data);
}
return root;
}
int main(){
scanf("%d",&n);
int temp;
for(int i=0;i<n;i++){
scanf("%d",&temp);
data.push_back(temp);
}
//根据输入序列建立AVL树,并且返回根节点
node*root=createAVL(data);
printf("%d",root->data);
return 0;
}