是否同一棵二叉搜索树 - 二叉搜索树
前言
让人印象深刻的一道题,因为Yes"写成了"YES",改错改了一下午....
题目
给定一个插入序列就可以唯一确定一棵二叉搜索树。然而,一棵给定的二叉搜索树却可以由多种不同的插入序列得到。例如分别按照序列{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
思路
- 根据插入序列构建二叉搜索树,在构建的时候有个小小的瑕疵...见代码
- 递归判断两棵二叉搜索
实现
完整代码
//不同的二叉搜索树可以给出相同的遍历序列,所以显然不能通过遍历来确定,那就通过子树来确定
#include<iostream>
using namespace std;
typedef struct Node {
int data;
struct Node *left;
struct Node *right;
} Node;
void myInput(Node *&t, int num) {
Node *p = t;
int temp;
cin >> temp;
//根结点手动存入
p->data = temp;
p->left = NULL;
p->right = NULL;
for (int i = 0; i < num - 1; i++) {
cin >> temp;
p = t;
Node *q = p; //又是那个老错误,不能直接这样指向,这是直接赋予一个新的值了
int flag;
while (p != NULL) {
q = p;
if (temp < p->data) {
p = p->left;
flag = 0;
}
else {
p = p->right;
flag = 1;
}
}
if (flag == 0) { //这就是那个小瑕疵,因为每次确定p时都是指向的待插入的位置,这是如果直接
q->left = new Node;
p = q->left;
}
else if (flag == 1) {
q->right = new Node;
p = q->right;
}
p->data = temp;
p->left = NULL;
p->right = NULL;
}
}
bool isSame(Node *t1, Node *t2) { //用递归去判断是否为同一颗树,对传入的两个结点进行判断
if (t1 == NULL && t2 == NULL)
return true;
else if (t1->data == t2->data)
return isSame(t1->left, t2->left) && isSame(t1->right, t2->right);
else
return false;
}
int main(){
int N, L;
while (1) {
cin >> N;
if (N == 0)
break;
cin >> L;
Node *tree = new Node;
myInput(tree, N);
for (int i = 0; i < L; i++) {
Node *test = new Node; //每次测试完都重置
myInput(test, N);
bool res = isSame(tree, test);
if (res == true)
cout << "Yes" << endl;
else
cout << "No" << endl;
}
}
return 0;
}
构建二叉搜索树
构建二叉树每插入一个新结点都需要通过从根结点开始比较找到插入的位置,问题在于,每次找到插入位置的时候,p指针都是直接指向待插入的位置的,这时如果直接申请新空间,就相当于为p重新指向了,而不是真的插入了结点。想了半天也没想到什么好的办法,所以用了很笨的法子,留了一个前驱指针。如果有更好的办法,请告诉我,感谢!
错误
void myInput(Node *&t, int num) {
Node *p = t;
int temp;
cin >> temp;
//根结点手动存入
p->data = temp;
p->left = NULL;
p->right = NULL;
for (int i = 0; i < num - 1; i++) {
cin >> temp;
p = t;
while (p != NULL) {
q = p;
if (temp < p->data) {
p = p->left;
flag = 0;
}
else {
p = p->right;
flag = 1;
}
}
p = new Node; //这就是那个问题,这里如果申请新结点的空间就相当于是重新赋值了,而不是连接在了树上
p->data = temp;
p->left = NULL;
p->right = NULL;
}
}
正确
void myInput(Node *&t, int num) {
Node *p = t;
int temp;
cin >> temp;
//根结点手动存入
p->data = temp;
p->left = NULL;
p->right = NULL;
for (int i = 0; i < num - 1; i++) {
cin >> temp;
p = t; //每次记得从根结点开始找
Node *q = p;
int flag;
while (p != NULL) {
q = p; //留的前驱结点
if (temp < p->data) {
p = p->left;
flag = 0;
}
else {
p = p->right;
flag = 1;
}
}
if (flag == 0) {
q->left = new Node;
p = q->left;
}
else if (flag == 1) {
q->right = new Node;
p = q->right;
}
p->data = temp;
p->left = NULL;
p->right = NULL;
}
}
递归判断
其实和遍历一样,所以说遍历真的是要掌握的基础算法...
bool isSame(Node *t1, Node *t2) { //用递归去判断是否为同一颗树,对传入的两个结点进行判断
if (t1 == NULL && t2 == NULL)
return true;
else if (t1->data == t2->data)
return isSame(t1->left, t2->left) && isSame(t1->right, t2->right);
else
return false;
}