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;
}