当前位置:   article > 正文

GPLT-天梯赛 二叉树&&二叉搜索树&&树的部分

gplt

1、7-12 这是二叉搜索树吗? (25 分)

在这里插入图片描述
思路:该题给出了二叉搜索树的前序遍历,然后题目要求我们判断他是否是一个二叉树,是的话就输出后序遍历,否则输出NO。对于前序二叉搜索树,我们可以知道先遍历左子树,再遍历右子树,那么从根节点开始往后找到第一个大于等于根节点的节点就是右子树根节点,同理从右往左找到第一个小于根节点的节点就是左子树的根节点,镜面则直接相反,开一个vector在搜完左树和右树后存下根节点,这个相当于模拟后序遍历过程了,然后判断是否是二叉搜索树,就直接让看vector的容量是否达到了n即可,总的来说是一道模拟题。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3+10;
int n;
int t[maxn];
vector<int> v;
int flag;
void dfs(int l, int r){
	if(l>r) return;
	int i=l+1,j=r;
	if(!flag){
		while(i<=r&&t[l]>t[i]) i++;
		while(j>l&&t[l]<=t[j]) j--;
	}
	else{
		while(i<=r&&t[l]<=t[i]) i++;
		while(j>l&&t[l]>t[j]) j--;
	}
	dfs(l+1,j);
	dfs(i,r);
	v.push_back(t[l]);
}
int main(){
	cin >> n;
	for(int i=1; i<=n; i++) scanf("%d",&t[i]);
	dfs(1,n);
	if(v.size()==n){
		puts("YES");
		for(int i=0; i<v.size(); i++){
			if(i!=0) printf(" ");
			printf("%d",v[i]);
		}
	}
	else{
		flag=1;
		v.clear();
		dfs(1,n);
		if(v.size()==n){
			puts("YES");
			for(int i=0; i<v.size(); i++){
				if(i!=0) printf(" ");
				printf("%d",v[i]);
			}
		}
		else puts("NO");
	}
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

2、7-13 树的遍历 (25 分)
在这里插入图片描述
思路:根据后序和中序恢复树并求他的前序遍历,简单题

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int n;
int mid[maxn],beh[maxn];
struct node{
	int l,r;
}t[maxn];
int build(int lm, int rm, int lb, int rb){
	if(lm>rm) return 0;
	int u=beh[rb],i=lm;//注意一下这里的u树根节点的值
	while(i<=rm&&mid[i]!=u) i++;
	t[u].l=build(lm,i-1,lb,rb-(rm-i+1));
	t[u].r=build(i+1,rm,rb-(rm-i),rb-1);
	return u;
}
void bfs(int u){
	queue<int> q;
	q.push(u);
	int flag=0;
	while(!q.empty()){
		int tt=q.front();
		q.pop();
		if(flag) printf(" ");
		printf("%d",tt);
		flag=1;
		if(t[tt].l) q.push(t[tt].l);
		if(t[tt].r) q.push(t[tt].r);
	}
}
int main(){
	cin >> n;
	for(int i=1; i<=n; i++) cin >> beh[i];
	for(int i=1; i<=n; i++) cin >> mid[i];
	int root=build(1,n,1,n);
	bfs(root);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

3、7-16 二叉搜索树的最近公共祖先 (25 分)在这里插入图片描述

思路:二叉树搜索树的一个经典问题,对于这道题而言,首先我们注意到键值是整数,也就是说会出现负数的情况,那么我们就不能恢复树,我们可以利用二叉搜索树的性质做题

首先,我们排除三种键值不存在的情况并输出答案,这个可以利用map完成
然后,就是我们利用二叉搜索树进行递归判断:
1、如果x和y其中有一个能够相等于当前最左端节点,那么x或y就是对方的祖先
2、如果x和y分别小于和大于当前根节点,那么就可以说明当前根节点是其祖先节点
3、以上情况没成立,那就找到左子树和右子树的分界点,进行下一步的递归判断

#include <bits/stdc++.h>
#define MAX 0x3f3f3f3f
using  namespace std;
typedef long long ll;
const int maxn = 1e5+10;
int n,m,k,x,y;
int a[maxn];
map<int,int> mp;
void judge(int l, int r){
	if(a[l]==x){
		printf("%d is an ancestor of %d.\n",x,y);
	}
	else if(a[l]==y){
		printf("%d is an ancestor of %d.\n",y,x);
	}
	else if((a[l]>x&&a[l]<y)||(a[l]>y&&a[l]<x)){
		printf("LCA of %d and %d is %d.\n",x,y,a[l]);
	}
	else{
		int i=r;
		while(i>l&&a[i]>a[l]) i--;//找到右子树和左子树的分界点 
		if(x<a[l]&&y<a[l]) judge(l+1,i);//全部属于左树,就到左树寻找 
		else judge(i+1,r);//否则全部去右树寻找 
	}
}
int main(){
	scanf("%d%d",&m,&n);
	for(int i=1; i<=n; i++){
		scanf("%d",&a[i]);
		mp[a[i]]++;	
	}
//	scanf("%d",&k);
	while(m--){
		scanf("%d%d",&x,&y);
		if(!mp[x]&&!mp[y]){
			printf("ERROR: %d and %d are not found.\n",x,y);
		}
		else if(!mp[x]){
			printf("ERROR: %d is not found.\n",x);
		}
		else if(!mp[y]){
			printf("ERROR: %d is not found.\n",y);
		}
		else{
			judge(1,n);
		}
	}
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

4、7-11 玩转二叉树 (25 分)
在这里插入图片描述
思路:经典建树,看代码,不解释

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
struct node{
	int l,r;
}t[maxn];
int n;
int mid[maxn],pre[maxn];
int build(int lm, int rm, int lp, int rp){
	if(lm>rm) return 0;
	int i=lm,u=pre[lp];
	while(i<=rm&&mid[i]!=u) i++;
	t[u].l=build(lm,i-1,lp+1,rp-(rm-i));//注意一下这里的下标后前序有所不同
	t[u].r=build(i+1,rm,rp-(rm-i-1),rp);
	return u;
}
void bfs(int u){
	queue<int> q;
	q.push(u);
	int flag=0;
	while(!q.empty()){
		int tt=q.front();
		q.pop();
		if(flag) printf(" ");
		flag=1;
		printf("%d",tt);
		if(t[tt].r) q.push(t[tt].r);
		if(t[tt].l) q.push(t[tt].l);
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1; i<=n; i++) cin >> mid[i];
	for(int i=1; i<=n; i++) cin >> pre[i];
	int root=build(1,n,1,n);
	bfs(root);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

5、7-15 完全二叉树的层序遍历 (25 分)
在这里插入图片描述
思路:这题的思路比较奇怪,我也是参考了别人的代码写出来的。先乘2往下找,再乘2+1往下找,最后把他加到数组里,递归查找所生成的数组就是二叉树的层序遍历了。
这应该是一个规律题。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int t[maxn], res[maxn], n,cnt=1;
void dfs(int p){
	if(p>n) return;
	dfs(p<<1);
	dfs(p<<1|1);
	res[p]=t[cnt++];
}
int main(){
	scanf("%d",&n);
	for(int i=1; i<=n; i++) scanf("%d",&t[i]); 
	dfs(1);
	for(int i=1; i<=n; i++){
		if(i!=1) printf(" ");
		printf("%d",res[i]);
	}
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

6、7-8 完全二叉搜索树 (25 分)
在这里插入图片描述
思路:这题和上一题一样,找二叉搜索树的层序遍历,不过给的是一个序列,然后我们将他排成中序,恢复即可。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int t[maxn],a[maxn];
int n;
int cnt;
void dfs(int u){
	if(u>n) return;
	dfs(u<<1);
	t[u]=a[++cnt]; 
	dfs(u<<1|1);
}
int main(){
	cin >> n;
	for(int i=1; i<=n; i++) cin >> a[i];
	sort(a+1,a+1+n);
	dfs(1);
	for(int i=1; i<=cnt; i++){
		if(i!=1) printf(" ");
		printf("%d",t[i]);
	}
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

一个总结:
通过上两题和我们的规律寻找可以发现,我们可以通过完全二叉树的前序、中序、后序遍历恢复他的层序遍历,这个完全二叉树是包括了完全二叉搜索树的。下面是代码总结

一般完全二叉树的先序和后序和中序是会给你的,或者说是完全二叉树的话就考你快排可以得到一个
完全二叉搜素树的中序

//t是答案数组,a是信息数组(就是先序等序列)
void dfs(int u){
	if(u>n) return;
	//先序
	t[u]=a[++cnt]; 
	dfs(u<<1);
	dfs(u<<1|1);
	
	//中序
	dfs(u<<1);
	t[u]=a[++cnt]; 
	dfs(u<<1|1);

	//后序
	dfs(u<<1);
	dfs(u<<1|1);
	t[u]=a[++cnt]; 
}
这样我们的t数组就是层序遍历的结果
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

7、7-11 是否完全二叉搜索树 (25 分)
在这里插入图片描述
思路:首先要知道什么是完全二叉树。完全二叉树是从1~n都用满了的树,即中间没有空节点。所以我们先递归建好搜索树,然后再利用这一个性质直接从1到maxn遍历n个节点并看他是否是连续的即可,同时这个输出的也是层序遍历的顺序。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int t[maxn];
int a[maxn];
int n;
void build(int u, int val){
	if(t[u]==0){
		t[u]=val;
		return;
	}
	else if(val>t[u]) build(u<<1,val);
	else if(val<=t[u]) build(u<<1|1,val);
}
int main(){
	cin >> n;
	for(int i=1; i<=n; i++){
		int x;
		cin >> x;
		build(1,x);
	}
	int cnt=1;
	int flag=0;
	for(int i=1; i<=1e5+10; i++){
		if(t[i]!=0){
			if(cnt!=1) printf(" ");
			printf("%d",t[i]);
			if(cnt==n) break;
			cnt++;
		}
		else flag=1;
	}
	puts("");
	if(flag) puts("NO");
	else puts("YES");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

8、7-9 二叉搜索树的2层结点统计 (25 分)
在这里插入图片描述

思路:直接建立二叉树,同时记录最大的节点,找到他对应的2次幂的范围就可以找到最下面两层节点的编号范围了,这样统计不为一个特殊数的节点个数就是最下面两层节点的个数了,当然会t,但是,我们合理剪枝,如果编号太大了,那就直接默认两个即可。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e7+10;
int n;
int t[maxn];
int a[maxn];
int maxx;
int flag;
void build(int u, int val){
	if(flag) return;
    if(u>=1000001){
        flag=1;
        return;
    }
	if(t[u]==-1001){
		t[u]=val;
		maxx=max(maxx,u);
		return;
	}
	else if(t[u]>=val) build(u<<1,val);
	else build(u<<1|1,val);
}
int main(){
	cin >> n;
	for(int i=1; i<=n; i++) scanf("%d",&a[i]);
	for(int i=0; i<=10000009; i++) t[i]=-1001;
//	cout << maxn  << endl;
	for(int i=1; i<=n; i++){
		build(1,a[i]);
	}
	int cnt=0;
	while(maxx>pow(2,cnt)-1){
		cnt++;
	}
//	cout << cnt << endl;
	int sum=0;
	for(int i=pow(2,cnt)-1; i>pow(2,cnt-2)-1; i--){
		if(t[i]!=-1001){
			sum++;
//			cout << i << endl;
		}
	}
	if(flag){
		printf("2");
		return 0;
	}
	printf("%d",sum);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/232710
推荐阅读
相关标签
  

闽ICP备14008679号