引言
给定一个插入序列就可以唯一确定一颗二叉搜索树(Binary Research Tree).但是,同一颗二叉搜索树可以由多种不同的插入序列方式得到。
例如:下面的二叉搜索树:可以分别由序列{3 1 2 4}和序列{3 1 4 2}得到。
提问:对于不同的输入序列,怎么判断他们生成的是否是同一颗二叉搜索树?
方法一
解题思路
最直接的方法,分别用输入的序列构造不同的树,再同时遍历和对比这两颗二叉搜索树,最后判断是否为同一颗树。
#include <stdio.h>
#include <malloc.h>
typedef struct TNode *Tree;
typedef int ElementType;
struct TNode{
ElementType element;
Tree Left,Right;
};
void FreeTree(Tree T);
Tree BuildTree(int N);
int IsSameTree(Tree T1,Tree T2);
Tree NewNode(int V);
Tree insert(Tree T,int V);
int main(void){
int N,L,i;
Tree T1;
scanf("%d",&N);//结点个数/序列个数
if (N){
scanf("%d",&L);//要验证的树的数量
T1=BuildTree(N);
for(i=0;i<L;i++){
Tree T2=BuildTree(N);
if(IsSameTree(T1,T2)){
printf("YES\n");
} else{
printf("NO\n");
}
FreeTree(T2);
}
}
FreeTree(T1);
return 0;
}
Tree BuildTree(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;
}
Tree NewNode(int V){
Tree T=(Tree)malloc(sizeof (struct TNode));//申请一个结点空间
T->element=V;
T->Left=NULL;
T->Right=NULL;
return T;
}
Tree insert(Tree T,int V){
if(!T){
T= NewNode(V);//如果树是空的,那么创建第一个结点
} else if(V<T->element){
T->Left= insert(T->Left,V);//树不是空的,并且传入的值 小于 当前结点值,那么这个结点左子树等于左子树中插入后得到的新的左子树
} else if(V>T->element){
T->Right=insert(T->Right,V);//树不是空的,传入值 大于 当前结点值,那么这个结点右子树等于右子树插入后得到的右子树(直到插入到最下面结点为止)
}
return T;
}
int IsSameTree(Tree T1,Tree T2){//核心对比函数
int flag;//标记是否相同,0表示一致,1表示不一致
if(T1==NULL && T2==NULL){//同时为空,认为它们相同
flag=1;
} else if(T1 && T2){//同时不为NULL,进一步分别比较左子树和右子树
if(T1->element == T2->element){
flag= (IsSameTree(T1->Left,T2->Left) && IsSameTree(T1->Right,T2->Right));
} else{
flag=0;
}
} else{//其他情况,返回不相同(比如T1或者T2中其中一个为NULL)
flag=0;
}
return flag;
}
void FreeTree(Tree T){//释放内存
if(T->Left){
FreeTree(T->Left);
}
if(T->Right){
FreeTree(T->Right);
}
free(T);
}
方法二
解题思路
只构造一棵二叉搜索树,然后把要验证的序列跟已经构造好的二叉搜索树进行遍历对比。具体是让序列中每一个需要比较的元素在二叉搜索树中走一遍,知道找到相同数值的结点。如果图中走到某一个没有访问过的结点,并且这个结点的值与当前结点元素的值不相同,那么可以判断这个序列能构造的不是同一颗二叉搜索树。
typedef struct TreeNode *Tree;//创建一个结点指针
struct TreeNode{
int v;
Tree Left,Right;
int flag;//用来标记遍历的时候是否走过此节点。0表示没有走过,1表示走过了
};
Tree MakeTree(int N);
void ResetTree(Tree T);
void FreeTree(Tree T);
Tree NewNode(int V);
Tree insert(Tree T,int V);
int check(Tree T,int V);
int Judge(Tree T,int N);
int main(void){
int N,L,i;
Tree T;
scanf("%d",&N);//树的结点个数
if(N){
scanf("%d",&L);//需要检验的树的个数
T=MakeTree(N);
for(i=0;i<L;i++){
if(Judge(T,N)){
printf("YES\n");
} else{
printf("NO\n");
ResetTree(T);//重置树的flag使其变为0
}
}
FreeTree(T);
scanf("%d",&N);//当N=0的时候,表示读入结束
}
return 0;
}
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;
}
Tree NewNode(int V){//创建一个新的树节点
Tree T=(Tree)malloc(sizeof (struct TreeNode));//申请一个结点空间
T->v=V;
T->Left=NULL;
T->Right=NULL;
T->flag=0;//没被访问过则为0,访问过了则设为1
return T;
}
Tree insert(Tree T,int V){//将新的结点插入到树中
if(!T){
T= NewNode(V);//如果树是空的,那么创建第一个结点
} else if(V<T->v){
T->Left= insert(T->Left,V);//树不是空的,并且传入的值 小于 当前结点值,那么这个结点左子树等于左子树中插入后得到的新的左子树
} else if(V>T->Right){
T->Right=insert(T->Right,V);//树不是空的,传入值 大于 当前结点值,那么这个结点右子树等于右子树插入后得到的右子树(直到插入到最下面结点为止)
}
return T;
}
//核心函数
int Judge(Tree T,int N){
int i,V;
int flag=0;//0表示读得过程中是一致的,1表示不一致(PS:这里的flag与定义的TreeNode里面的flag名字相同,但是表示不一样的内容)
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))){// 如果发现序列中的某个数与T不一致时,必须把序列后面的数都读完
flag = 1;
}
}
if(flag){
return 0;
} else{
return 1;
}
}
int check(Tree T,int V){//判断是否为同一搜索树
if(T->flag){//如果flag的值为1,那么代表这个结点已经走过了,那么需要往下面走
if(V<T->v){//这个判断的值小于结点值,那么往左边判断
return check(T->Left,V);
} else if(V>T->v){//值大于结点值,那么往右边判断
return check(T->Right,V);
} else{//如果结点走过了但是又来了一个新的值,那么说明传入的序列中有两个值都相等,此时认为不是同一搜索树
return 0;
}
} else{
if(V==T->v){//结点没走过,并且恰好就是这个结点,那么把这个结点标注并且返回 1
T->flag=1;
return 1;
} else{
return 0;
}
}
}
void ResetTree(Tree T){//重置搜索树中的flag
if(T->Left){
ResetTree(T->Left);
}
if(T->Right){
ResetTree(T->Right);
}
T->flag=0;
}
void FreeTree(Tree T){//释放内存
if(T->Left){
FreeTree(T->Left);
}
if(T->Right){
FreeTree(T->Right);
}
free(T);
}
如果大家有好的想法,欢迎在评论区交流讨论... 原创不易,转载请标明出处