问题
给定一个插入序列就可以唯一确定一棵二叉搜索树。然而,一棵给定的二叉搜索树却可以由多种不同的插入序列得到。例如分别按照序列{2, 1,3}和{2, 3, 1}插入初始为空的二叉搜索树,都得到一样的结果。于是对于输入的各种插入序列,你需要判断它们是否能生成一样的二叉搜索树。
输入格式: 输入包含若干组测试数据。每组数据的第1行给出两个正整数N(≤10)和LL,分别是每个序列插入元素的个数和需要检查的序列个数。第2行给出N个以空格分隔的正整数,作为初始插入序列。最后L行,每行给出N个插入的元素,属于L个需要检查的序列。
输出格式: 对每一组需要检查的序列,如果其生成的二叉搜索树跟对应的初始序列生成的一样,输出“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
分析
一种直接的想法是,按第一个序列建一棵二叉查找树,然后接下来的每个序列都建一棵二叉查找树,接着和第一个树作对比,是否完全一致。按照建立二叉查找树的过程就不难实现,本文不再叙述。而这里讲述一种不建树的方法。
对于二叉搜索树,它的左子树的每个键值必定都小于根的键值,右子树的每个键值必定大于根的键值。左、右子树同理。由此,那么我们可以把的序列(3,1,2,4)分成左右两个子树,第一个输入的是3作为根,后边的值再输入时就分入左右两子树的序列,例如上述序列左子树序列是(1,2),右子树序列是(4),注意这里分属两个序列时是有顺序区别的,第一个进入序列的值是子树的根。然后再对两个子树分别做递归地求解,直到只剩下根结点时递归结束。
这样分析以来,根据二叉搜索树的数值特点,仅用数组表示数据即可实现,而不需要建立二叉搜索树的结构。
概要设计
主函数:
两个数组T1,T2,分别代表输入的第1棵树和后边待比较的各树。需要的函数有一个判断树是否相同的函数,传入这两个树作为参数,根据两个树是否相同返回值为1或0。
判断两树是否相同的函数:
实际传入的参数除了两个数组之外,还要显式地传入数组长度,如果数组长度都是不同的,则可以直接返回0说明不是同一棵二叉搜索树,可以控制对子树数组长度的判断。
程序设计
#include <stdlib.h>
#include <stdio.h>
#define SIZE 10
int judge(int *a,int x,int *b, int y);
int main(void)
{
int N,L;
int bst[SIZE],tgt[SIZE];
while(1){
scanf("%d",&N);
if(N==0)
break;
scanf("%d",&L);
for(int i=0;i<N;i++)
scanf("%d",bst+i);
while(L--){
for(int i=0;i<N;i++)
scanf("%d",tgt+i);
if(judge(bst,N,tgt,N))
printf("Yes\n");
else
printf("No\n");
}
}
return 0;
}
int judge(int *a,int x,int *b,int y)
{
if(x!=y)
return 0;
if(x==0 && y==0)//均为空树时
return 1;
if(x==1 && y==1){
if(a[0]==b[0])//仅有1个元素时,比较这个元素是否相同
return 1;
return 0;
}
else{
int lst1[SIZE],lst2[SIZE],rst1[SIZE],rst2[SIZE];
//表示左子树1,左子树2,右子树1,右子树2,
//每个树会裂分成两个子树
int sl1=0,sl2=0,sr1=0,sr2=0;
//分别表示各子树中的元素个数
int i;
//将传入的树分成左右子树
for(i=1;i<x;i++){
if (a[i]>a[0])
rst1[sr1++]=a[i];
else
lst1[sl1++]=a[i];
}
for(i=1;i<y;i++){
if(b[i]>b[0])
rst2[sr2++]=b[i];
else
lst2[sl2++]=b[i];
}
return judge(lst1,sl1,lst2,sl2) && judge(rst1,sr1,rst2,sr2);
}
}
反思与评价:
不需要建立树的结构,只需要做整型的数组就可以保存二叉树的数据,时间复杂度上,运用递归每次把数组分成两个,直到递归至平凡情况,平均时间复杂度是O(logN)。但最坏情况下,是输入一个有序的序列,这时,树就退化成了链表,时间复杂度增长成O(N)
测试时要包括几种边界的测试,比如单侧子树、不同的根值、最大N、最小N等数据需要都做测试。