2-1 是否同一棵二叉搜索树
分数 35
作者 陈越
单位 浙江大学
给定一个插入序列就可以唯一确定一棵二叉搜索树。然而,一棵给定的二叉搜索树却可以由多种不同的插入序列得到。例如分别按照序列{2, 1, 3}和{2, 3, 1}插入初始为空的二叉搜索树,都得到一样的结果。于是对于输入的各种插入序列,你需要判断它们是否能生成一样的二叉搜索树。
输入格式:
输入包含若干组测试数据。每组数据的第1行给出两个正整数N (≤10)和L,分别是每个序列插入元素的个数和需要检查的序列个数。第2行给出N个以空格分隔的正整数,作为初始插入序列。随后L行,每行给出N个插入的元素,属于L个需要检查的序列。
简单起见,我们保证每个插入序列都是1到N的一个排列。当读到N为0时,标志输入结束,这组数据不要处理。
输出格式:
对每一组需要检查的序列,如果其生成的二叉搜索树跟对应的初始序列生成的一样,输出“Yes”,否则输出“No”。
输入样例:
4 2
3 1 4 2
3 4 1 2
3 2 4 1
2 1
2 1
1 2
0
输出样例:
Yes
No
No
鸣谢青岛大学周强老师补充测试数据!
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
C++ (g++)
思路:
二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树:
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
它的左、右子树也分别为二叉排序树。
建一棵树:将比对的基准序列建树,待比较序列不必建树,在树上按次序搜索待比较序列的元素,每搜到一个元素则在树上作出标记,如果在搜索序列元素的过程中,搜到了之前未搜过的元素(序列后面的元素比序列前面的元素早一步被搜到)(遍历到了未被标记的结点),则该序列与树不一致。
原理是:按照序列建成的树再按次序在树中搜索该序列中的元素时,搜索过程中不会出现序列前面的元素没搜到就搜到序列后面的元素这样的情况的。
那么我们定义一个结构体,内置变量v存放根结点的数据,左子树,右子树,flag用于标志是否被遍历到。
创建几个函数
Tree NewNoede(int v):用于建立新结点
Tree Insert(Tree T,int v):用于插入新结点。
Tree MakeTree(int N):用于生成二叉搜索树
int check(Tree T,int v):判断序列中的特定元素是否应该出现在树的对应位置
int judge(Tree T,int N):判断序列是否与树一致
void Reset(Tree T):清空树的标记
void FreeT(Tree T):释放树所占用的空间
代码样例:
#include<bits/stdc++.h>
using namespace std;
typedef struct TreeNode *Tree;
struct TreeNode{
int v;
Tree Left;
Tree Right;
int flag;
};
Tree NewNode(int v){
Tree T=(Tree)malloc(sizeof(struct TreeNode));
T->v=v;
T->Left=T->Right=NULL;
T->flag=0;//没有被搜索过
return T;
}
Tree Insert(Tree T,int v){
if(!T)//如果树为空的话,直接开辟以v为根结点的树
T=NewNode(v);
else{
if(v>T->v)//如果v大于根结点,则插入右子树
T->Right=Insert(T->Right,v);
else
T->Left=Insert(T->Left,v);
}
return T;
}
Tree MakeTree(int N){
Tree T;
int i,v;
scanf("%d",&v);
T=NewNode(v);//创建新节点
for(i=1;i<N;i++){
scanf("%d",&v);
T=Insert(T,v);//做插入操作
}
return T;
}
int check(Tree T,int v){
//先查看v是否与已经被访问过的结点相等
if(T->flag){
if(v<T->v)
return check(T->Left,v);
else if(v>T->v)
return check(T->Right,v);
else
return 0;//如果相等的话,返回0,错误,二叉搜索树不能出现相同结点
}
//再查看v是否与还未被访问过的结点相等
else{
if(v==T->v){//该结点之前没有被搜索过,且刚好等于这个元素
T->flag=1;//重置标记
return 1;//符合
}else
return 0;
}
}
int Judge(Tree T,int N){//一定要将序列都遍历完,如果没有遍历完就return,则序列1剩余的元素会被当作序列2读入
int i,v,flag=0;
//flag=0代表目前与树还一致,1代表目前与 树不一致
scanf("%d",&v);
if(v!=T->v)
flag=1;//该元素不等于树根,所以一定不一致
else
T->flag=1;//该元素等于树根,暂时标记为一致
for(i=1;i<N;i++){
scanf("%d",&v);
//如果前面以及不一致了,则不需要检查后面的元素了,直接得到序列不一致的结论
if(!flag&&!check(T,v))
flag=1;
}
if(flag)
return 0;
else
return 1;
}
void ResetT(Tree T){
if(T->Left)
ResetT(T->Left);
if(T->Right)
ResetT(T->Right);
T->flag=0;
}
void FreeTree(Tree T){
if(T->Left)
FreeTree(T->Left);
if(T->Right)
FreeTree(T->Right);
free(T);
}
int main(){
int N,L,i;//N每个序列的元素个数 L序列个数
scanf("%d",&N);
Tree T;
while(N){//以N为0作为数据输入截止条件
scanf("%d",&L);
T=MakeTree(N);
for(i=0;i<L;i++){
if(Judge(T,N))
printf("Yes\n");
else
printf("No\n");
ResetT(T);//清楚T中的标记flag
}
FreeTree(T);//要内存达到最优需要释放
scanf("%d",&N);
}
return 0;
}