PAT树专题

1.已知后序和中序,求层序输出——利用两序建树,后bfs输出树上结点值

例题 PAT甲 1020 Tree Traversals (25 分)

https://pintia.cn/problem-sets/994805342720868352/problems/994805485033603072

int pos[35],in[35];
node* root=build(0,n-1,0,n-1);
bfs(root);

struct node {
	int val;
	node *left,*right;
} tr[35]
node* build(int posL,int posR,int inL,int inR) {
	if(posL>posR) return NULL;
	node* r=(struct node*)malloc(sizeof (struct node*));
	r->val=pos[posR];
	int k=inL;
	while(pos[posR]!=in[k]) k++;
	int numLeft=k-inL;
	r->left=build(posL,posL+numLeft-1,inL,k-1);
	r->right=build(posL+numLeft,posR-1,k+1,inR);
	return r;  //!!!
}
void bfs(node* r) {
	int cnt=0;
	queue<node*> q;
	q.push(r);
	while(!q.empty()) {
		node* p=q.front();
		q.pop();
		cout<<p->val;
		cnt++;
		if(cnt<n) cout<<" ";
		if(p->left!=NULL) q.push(p->left);
		if(p->right!=NULL) q.push(p->right);
	}
}

2.已知前序和中序(进栈前序,出栈中序),输出后序遍历——建树,递归后序遍历

例题:1086 Tree Traversals Again (25 分)

https://pintia.cn/problem-sets/994805342720868352/problems/994805380754817024

vector<int>pre,in;
stack<int> st;
node* r=build(0,n-1,0,n-1);
post(r);

node* build(int preL,int preR,int inL,int inR){
	if(preL>preR) return NULL;
	node* r=(struct node*)malloc(sizeof(struct node));
	r->val=pre[preL];
	int k=inL;
	while(pre[preL]!=in[k]) k++;
	int numLeft=k-inL;
	r->left=build(preL+1,preL+numLeft,inL,k-1);
	r->right=build(preL+numLeft+1,preR,k+1,inR);
	return r;
}
void post(node* r){
	if(r==NULL) return;
	post(r->left);
	post(r->right);
	cout<<r->val;
	cnt++;
	if(cnt<n) cout<<' ';
}

不建树版的

post(0,0,n-1);
void post(int root,int inL,int inR){
	if(inL>inR) return;
	int k=inL;
	while(pre[root]!=in[k]) k++;
	int numLeft=k-inL;
	post(root+1,inL,k-1);
	post(root+numLeft+1,k+1,inR);
	cout<<pre[root];
	cnt++;
	if(cnt<n) cout<<" "; 
}

已知前序中序,输出后序第一个元素。 

#include<bits/stdc++.h>
using namespace std;
int pre[50005],in[50005];
int flag;
void PostOrder(int prel,int inl,int inr){
	if(inl>inr||flag==1) return;
	int i=inl;
	while(pre[prel]!=in[i]) i++;
	PostOrder(prel+1,inl,i-1);
	PostOrder(prel+i-inl+1,i+1,inr);
	if(flag==0){
		printf("%d",in[i]);
		flag=1;
	}
}
int main(){
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
		scanf("%d",&pre[i]);
	for(int i=0;i<n;i++)
		scanf("%d",&in[i]);
	PostOrder(0,0,n-1);
	return 0;
} 
/*
0	1	2	3	4	5	6
prel      		in
     prel+1      
*/

3.已知前序和后序,问中序是否唯一并打印中序结果(或符合的结果之一)

  • 用unique标记是否唯一
  • 已知二叉树的前序和后序是无法唯一确定一颗二叉树的,因为一个结点可能是根的左孩子也有可能是根的右孩子
  • 如果发现了一个无法确定的状态(if(k-preL>1)),置unique = false
  • 题目只需要输出一个方案——假定这个不可确定的孩子的状态是右孩子
  • 如何求根结点和左右孩子划分?首先我们需要知道树的表示范围,需要四个变量,分别是前序的开始的地方prel,前序结束的地方prer,后序开始的地方postl,后序结束的地方postr,前序的开始的第一个应该是后序的最后一个是相等的,这个结点就是根结点,以后序的根结点的前面一个结点作为参考寻找这个结点在前序的位置,就可以根据这个位置来划分左右孩子,递归处理
int pre[35],post[35];
vector<int> in;
int unique=1;
getIn(0,n-1,0,n-1);

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 k=preL;
		while(pre[k]!=post[postR-1]) k++;
		if(k-preL>1)//若不符合条件则该节点可能是右子树根节点,也可能是左子树根节点
			getIn(preL+1,k-1,postL,postL+(k-preL-1)-1);
		else unique=0;
		in.push_back(post[postR]);
		getIn(k,preR,postL+(k-preL-1),postR-1);
	}
}

4.输入各个节点的子节点,求反转后的层序遍历和中序遍历——入数组,做标记,找根节点,数组下标传参,无需反转只需在遍历时改为先右后左即可

参考例题:1102 Invert a Binary Tree (25 分)

https://pintia.cn/problem-sets/994805342720868352/problems/994805365537882112

cin>>n;
char ch=getchar();
char a,b;
for(int i=0; i<n; i++) {
	scanf("%c %c\n",&a,&b);  //最后一行ctrl+z才出结果,但完全正确!!!  :)
	tree[i].val=i;
	if(a!='-') {
		vis[a-'0']=1;
		tree[i].left=a-'0';
	}
	if(b!='-') {
		vis[b-'0']=1;
		tree[i].right=b-'0';
	}
}

for(int i=0; i<n; i++) {          //读入数据,是否含回车,scanf/cin易错!!!
	cin>>a>>b;
	tree[i].val=i;
	if(a[0]!='-') {
		vis[stoi(a)]=1;
		tree[i].left=stoi(a);
	}
	if(b[0]!='-') {
		vis[stoi(b)]=1;
		tree[i].right=stoi(b);
	}
}
for(int i=0; i<n; i++)            //找根节点
	if(vis[i]==0) root=i;
Level(root);
cnt=0;
inOrder(root);

void Level(int root) {            //全程无指针,仅需下标入队列
	queue<int> q;
	q.push(root);
	int p;
	while(!q.empty()) {
		p=q.front();
		q.pop();
		cout<<tree[p].val;
		cnt++;
		if(cnt<n) cout<<' ';
		if(tree[p].right!=-1) q.push(tree[p].right);
		if(tree[p].left!=-1) q.push(tree[p].left);
	}
	cout<<endl;
}
void inOrder(int r) {             //遍历前先判断(初始值为-1)
	if(tree[r].right!=-1) inOrder(tree[r].right);
	cout<<tree[r].val;
	cnt++;
	if(cnt<n) cout<<' ';
	if(tree[r].left!=-1) inOrder(tree[r].left);
}

5.给出根节点及各节点的子节点情况,求深度最小的叶子结点的深度及个数——bfs,dfs

  • bfs,找到最小价格不能直接退出——因为要计最优价格的数量,故继续与后续价格比较
  • dfs,赋根节点价格(各节点实时更新)递归边界——到达子节点(两种情况计数)

参考题目  1106. Lowest Price in Supply Chain (25)

struct node {
	double p; //该节点价格
	vector<int> child; //可能无子节点——目标,或多个子节点
} store[100005];

void bfs() {
	queue<int> q;
	q.push(0);
	store[0].p=p;
	int root,s;
	while(!q.empty()) {
		root=q.front();
		q.pop();//(下一行)无子节点——目标,因为要计数,不可马上退出,与最小价格相比cnt有两种情况
		if(store[root].child.size()==0) {  			
                        if(ans>store[root].p) ans=store[root].p,cnt=1;
			else if(store[root].p==ans)cnt++;
		} else {
			for(int i=0; i<store[root].child.size(); i++) {
				s=store[root].child[i];
				store[s].p=store[root].p*(1+r); //初始时已做处理r=r/100
				//printf("**%.4lf %d\n",store[s].p,s);
				q.push(s);
			}
		}
	}
	printf("%.4lf %d\n",ans,cnt);
}
--------------------------------------------------------------------------------------
有结构体的dfs

store[0].p=p;
dfs(0);
void dfs(int root) {
	if(store[root].child.size()==0){
		if(store[root].p<ans) ans=store[root].p,cnt=1;
		else if(store[root].p==ans) cnt++;
		return;
	}
	int s;
	for(int i=0;i<store[root].child.size();i++){
		s=store[root].child[i];
		store[s].p=store[root].p*(1+r);
		dfs(s);
	}
}
--------------------------------------------------------------------------------------
无需结构体的dfs,单参数(下标,深度)

void dfs(int index, int depth) {
    if(mindepth < depth)
        return ;
    if(v[index].size() == 0) {
        if(mindepth == depth)
            minnum++;
        else if(mindepth > depth) {
            mindepth = depth;
            minnum = 1;
        }
    }
    for(int i = 0; i < v[index].size(); i++)
        dfs(v[index][i], depth + 1);
}

6.给出根节点及各节点的子节点情况,求深度最大的叶子结点的深度及个数(同上)

参考题目:1090. Highest Price in Supply Chain (25)

void dfs(int root,int depth){
	if(v[root].size()==0){
		if(maxdep<depth) maxdep=depth,cnt=1;
		else if(maxdep==depth) cnt++;
		return;
	}
	for(int i=0;i<v[root].size();i++)
		dfs(v[root][i],depth+1);
}

7.给出根节点及各节点的子节点情况,求所有叶子结点的总值数量x价格x涨价百分比)(同上)

参考题目:1079 Total Sales of Supply Chain (25 分)

注意:若该处为叶子结点,则该值为0,其后输入其销售数量

vector<int>v[100005];
int amount[100005];
void dfs(int root,int depth){
	if(v[root].size()==0){
		ans+=p*pow((1+r),depth)*amount[root];
		return;
	}
	for(int i=0;i<v[root].size();i++)
		dfs(v[root][i],depth+1);
}
-----------------------------------------------
注意若该处为叶子结点,则该值为0,其后输入其销售数量
if(t==0){
	cin>>num;
	amount[i]=num;
	continue;
}

8.输入各节点情况,求每一层叶结点个数

参考题目:1004 Counting Leaves (30 分)

https://pintia.cn/problem-sets/994805342720868352/problems/994805521431773184

注意:

  • maxd记录叶结点所在最大层
  • d++的条件
int depth[105]= {0};
vector<int> v[105];

dfs(1,1);
void dfs(int idx,int d) {
	if(v[idx].size()==0) {
		depth[d]++;         //是叶子节点d++
		maxd=max(maxd,d);   //确定最后最大叶子结点所在层数
		return;
	}
	for(int i=0; i<v[idx].size(); i++) 
		dfs(v[idx][i],d+1);
}
-------------------------------------------------------------------
二维向量清空操作
for(int i=0; i<105; i++)
	v[i].clear();
		
		
		

9.给出各结点子结点情况,求节点数最多的层数及其节点个数——只求数目,故dfs每遍历到一个点就将该层level【i】++,找最大level【i】(遍历就对了)

参考题目:1094 The Largest Generation (25 分)

void dfs(int idx,int depth) {
	level[depth]++;
	if(v[idx].size()==0) return;
	for(int i=0; i<v[idx].size(); i++)
		dfs(v[idx][i],depth+1);
}

10.根据输入顺序建二叉搜索树,输出最深的两层的结点之和的算式——建树,存depth

注意<=均为左子树

参考题目:1115 Counting Nodes in a BST (30 分)

https://pintia.cn/problem-sets/994805342720868352/problems/994805355987451904

struct node{          //建树
	int val;
	node *left,*right;
}tr[1005];
int depth[15]={0};   //各深度节点数目纪录
int maxd=-1;         //最大深度记录
node* root=NULL;
for(int i=0;i<n;i++){
	cin>>x;
	root=build(root,x);    //Fixed format
}
dfs(root,1);    

node* build(node* root,int x){
	if(root==NULL){
		root=(struct node*)malloc(sizeof(struct node));
		root->val=x;
		root->left=root->right=NULL;
		return root;
	}
	if(x<=root->val) root->left=build(root->left,x);
	else if(x>root->val)root->right=build(root->right,x);
	return root;
}
void dfs(node* root,int d){
	depth[d]++;
	maxd=max(maxd,d);
	if(root->left==NULL&&root->right==NULL) return;
	if(root->left!=NULL) dfs(root->left,d+1);
	if(root->right!=NULL) dfs(root->right,d+1); 
}

11.给出各节点序号及其值、总W,按从大到小的顺序打印 满足总值为W的分支(末端必须为到叶节点)的各个节点的值的序列

参考题目:1053 Path of Equal Weight (30 分)

https://pintia.cn/problem-sets/994805342720868352/problems/994805424153280512

各节点入vector后排序+dfs

int ans[100005];
struct node {
	int val;
	vector<int> v;
} tr[105];

bool cmp(int a,int b) {  //存进去的是下标,比较的依据为下标所对应的值的大小
	return tr[a].val>tr[b].val;
}
ans[0]=tr[0].val; //根节点必在
dfs(0,tr[0].val,1);

void dfs(int id,int totalW,int pos) {
	if(totalW==W) {
		if(tr[id].v.size()==0) { //末尾必须为叶子结点
			for(int i=0; i<pos; i++) {
				if(i) cout<<' ';
				cout<<ans[i];
			}
			cout<<endl;
		}
		return;
	}
	node now=tr[id];
	for(int i=0; i<now.v.size(); i++) {
		if(totalW+tr[now.v[i]].val<=W) {
			ans[pos]=tr[now.v[i]].val;  //如果符合要求该点值计入ans【】
			dfs(now.v[i],totalW+tr[now.v[i]].val,pos+1);
		}
	}
}

	

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值