PTA写BUG日志——关于数据结构里面的“树”(算法笔记上机实战)

二叉树的遍历

  • 先序遍历
  • 中序遍历
  • 后序遍历
  • 层序遍历

经典问题1:给出两个遍历树的序列,如何复现树?🎄
:一定会需要中序遍历来区分左右子树,中序遍历+(剩余3种任一遍历的数组即可复现原来的树的结构)

练习1

PTA甲级1020

这题是根据中序和后序来复现树的结构,再用层次遍历输出即可,思路不是特别难,主要涉及复现函数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:树的建立(给出父节点)以及层次遍历

PTA甲级1090

这题和上一题思路大致相同,不是很难~😊主要的区别在于:

  • 建树的过程:本题是给出每个结点的父节点,所以输入的时候处理方法和上一题不同
  • 层次遍历中间的处理:为了得到最大值以及最大值个数,需要在每次从队列里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:层次遍历求层数以及求出结点数最大层的层号

PTA甲级1094

这题的突破口其实和前两题一样,主要改动还是在层次遍历的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:层次遍历,浮点数的判断大小(细节,但是真的很坑)

PTA甲级1106

这题论思路,和前两题一样一样的,主要需要解决浮点数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:

PTA甲级1053

这题整了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:考察先序遍历+中序遍历->建立树,以及后序遍历输出

PTA甲级1043

注意点,这题一开始我想用静态树结构,但是会出现死循环的情况,原因是输入的树的结点值可以相同(还有负数情况),综上所以改用指针结构

  • 由于排序树的性质,题目隐含条件是中序遍历结果为输入数组的增序/降序,代码函数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混合在一起

PTA甲级1064

题解
感觉是一道比较难的题目,最开始的时候真的无从下手,整理一下思路,有两版(第一版是思路雏形,但是卡在一个测试点,第二版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甲级1099

题解

  • 发现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二叉平衡树

PTA甲级1066

这题希望大家回归书本,把二叉平衡树各个子函数的构建好好看一下再默一遍代码,这题我是照着书来的,所以之后肯定得回炉重造,因为默写得很烂,还是翻书🍠才找到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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值