L2-004 这是二叉搜索树吗?

                                            L2-004 这是二叉搜索树吗?

                        https://pintia.cn/problem-sets/994805046380707840/problems/994805070971912192

 

 

题意

一棵二叉搜索树可被递归地定义为具有下列性质的二叉树:对于任一结点,其左子树中所有结点的键值小于该结点的键值;其右子树中所有结点的键值大于等于该结点的键值;其左右子树都是二叉搜索树。所谓二叉搜索树的“镜像”,即将所有结点的左右子树对换位置后所得到的树。给定一个整数键值序列,现请你编写程序,判断这是否是对一棵二叉搜索树或其镜像进行前序遍历的结果。

输入

输入的第一行给出正整数 N(≤1000)。随后一行给出 N 个整数键值,其间以空格分隔。

输出

如果输入序列是对一棵二叉搜索树或其镜像进行前序遍历的结果,则首先在一行中输出 YES ,然后在下一行输出该树后序遍历的结果。数字间有 1 个空格,一行的首尾不得有多余空格。若答案是否,则输出 NO

样例输入

1)

7
8 6 5 7 10 8 11

2)

7
8 10 11 8 6 7 5

3)

7
8 6 8 5 10 9 11

样例输出

1)

YES
5 7 6 8 11 10 8

2)

YES
11 8 10 7 5 6 8

3)

NO

分析

使用前序遍历和中序遍历构造树,如果一颗树是而二叉搜索树,则它的中序遍历是非递减的,如果一棵树是二叉搜索树的镜像树,那么它的中序遍历是非递增的,我们可以通过这个性质以及题目给出的前序遍历求出树的中序遍历,先构造二叉搜索树,如果失败的话再进行构造二叉搜索树的镜像树,如果还是失败就输出“NO”,否则输出“YES”和构造成的树的中序遍历。

 

一棵二叉搜索树可被递归地定义为具有下列性质的二叉树:对于任一结点,其左子树中所有结点的键值小于该结点的键值;其右子树中所有结点的键值大于等于该结点的键值;其左右子树都是二叉搜索树。那么二叉搜索树的镜像树的可这样定义:对于任一结点,其左子树中所有结点的键值大于等于该结点的键值;其右子树中所有结点的键值小于该结点的键值;其左右子树都是二叉搜索树的镜像树。

 

由于题目给出的结点键值可能是一样的,因此在构造二叉搜索树的镜像树时,必须按照从后往前的顺序在中序遍历数组中找和根节点,这样和根节点键值相同的重复结点就会称为左子树,这样才符合二叉搜索树的镜像树,如果不这样的话,样例二不会通过。

 

C++程序

#include<iostream>
#include<algorithm>

using namespace std;

const int N=1005;

//结点 
struct Node{
	int val;
	Node *l,*r;
	Node(int val):val(val),l(NULL),r(NULL){}
};

//后序遍历 
bool flag;

void post(Node *x)
{
	if(x)
	{
		post(x->l);
		post(x->r);
		if(flag) printf(" ");
		printf("%d",x->val);
		flag=true;
	}
}

int pre[N],in[N];//前序遍历和中序遍历结果 

//是否能构造一棵二叉树 
bool legal;

//根据前序遍历和中序遍历构造二叉搜索树
// l,r表示pre的区间,a,b表示in的区间,dir表示从前往后找,还是从后往前找 
Node* build(int l,int r,int a,int b,bool dir)
{
	if(legal&&l<=r)
	{
		int rt=pre[l];
		int mid;
		if(dir)//从前往后找(用于构造二叉搜索树) 
		  for(mid=a;mid<=b&&in[mid]!=rt;mid++);
		else//从后往前找(用于构造二叉搜索树进行镜像操作后的树) 
		  for(mid=b;mid>=a&&in[mid]!=rt;mid--);
		if((dir&&mid>b)||(!dir&&mid<a))//构造失败 
		{
			legal=false;
			return NULL;
		} 
		Node *node=new Node(rt);
		node->l=build(l+1,mid-a+l,a,mid-1,dir);
		node->r=build(mid-a+l+1,r,mid+1,b,dir);
		return node; 
	}
	return NULL;
}

//销毁树 
void destroy(Node *x)
{
	if(x)
	{
		destroy(x->l);
		destroy(x->r);
		delete x;
		x=NULL;
	}
}

int main()
{
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%d",&pre[i]);
		in[i]=pre[i];
	}
	//二叉搜索树的中序遍历是非递减的 ,利用前序遍历和中序遍历构造二叉树 
	sort(in,in+n);
	legal=true;
	Node *root=build(0,n-1,0,n-1,true);
	//如果构造失败,说明不是二叉搜索树,进一步判断它是否为二叉树进行镜面操作后的树 
	if(!legal) 
	{
		destroy(root);//删除构造失败的树 
		reverse(in,in+n);//中序遍历是非第增的 
		legal=true;
		root=build(0,n-1,0,n-1,false);
	}
	if(legal)//构造二叉搜索树或二叉搜索树镜像后的树成功 
	{
		printf("YES\n");
		post(root);
	}
	else//失败 
	  printf("NO\n");
	destroy(root);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值