给定一个插入序列就可以唯一确定一棵二叉搜索树。然而,一棵给定的二叉搜索树却可以由多种不同的插入序列得到。例如分别按照序列{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
做了较大创新:用map实现树,空间开销只和节点数有关,达到级别,同时又部分保留了数组方式的便捷性(通过下标操作)。
具体而言,把key域作为下标,而value域用于保存节点信息,其插入方式与数组实现的树在逻辑上是一致的:如果当前访问下标t为空,则插入,否则进行比较:若插入值大于访问值,则访问2t+1,若插入值小于访问值,则访问2t。
比对的时候,借助了一个向量序列,以进一步优化效率。在构建比较基准树(第一棵树)时,插入前多一个动作:将下标存入序列。如果出现在基准树中的节点并没有被比较树使用,则可以立即判断两棵树不同,从而退出比较过程。
#include <iostream>
#include <vector>
#include <map>
using namespace std;
int main(void)
{
int n,l;
cin>>n;
while(n){
cin>>l;
map<int,int> p; //作为基准的树
vector<int> check; //用于检查的比较序列
for(int i=0;i<n;i++){
int x,t=1; //1代表根节点,从根节点开始
cin>>x;
while(p.find(t)!=p.end()){ //只要当前结点不为空
if(p[t]<x)t=t*2+1; //如果待插入元素更大,下标转向右子树
else t*=2; //否则转向左子树
}
p[t]=x; //找到空节点,插入
check.push_back(t); //把这个节点插入比较序列
}
for(int j=0;j<l;j++){ //按照题意,有多组插入序列待比较
map<int,int> q; //********************
int flg=1; //*
for(int i=0;i<n;i++){ //* 这里的操作基本相同
int x,t=1; //* 唯一区别是不操作比较序列
cin>>x; //* 比较序列有一个就行了
while(q.find(t)!=q.end()){ //* 比较序列是一个较好的设计
if(q[t]<x)t=t*2+1; //* 有利于效率的提高
else t*=2; //*
} //*
q[t]=x; //********************
}
for(int i=0;i<check.size();i++){ //这里开始检查
if(q.find(check[i])==q.end()){ //如果这个下标没有出现
flg=0;break; //毫无疑问不是同一棵树
}
else if(q[check[i]]!=p[check[i]]){ //如果下标同,值不同
flg=0;break; //也不是同一棵树
}
}
if(flg)cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
cin>>n;
}
return 0;
}