数据结构:判断是否为同一棵二叉搜索树
前言碎语
作为一个代码小白,在自学的过程中,最舒服的好像就是有讲解、有提示、甚至直接是有源码……然而,照着写永远是无法进步的,因为,很可能最后错了自己都不知道错在了哪!
就比如下面这个,照着MOOC小白教程写的,然,错了,并且错的一点头绪也没有。贴在这,警醒自己!!
/* 判断是否是同一颗二叉搜索树 */
/*
### 解题思路:
一种简单粗暴的方法,判断相同结点的左右两边子树是否按顺序相同。
一种比较笨的方法:按照给定的序列建一棵二叉搜索树,然后需要判断的再建一棵树,然后比较;
其实有了这个笨方法之后,就可以加以改进,比如知建一棵树,然后通过给定顺序来判断(这里就需要一个标志来记录)
*/
#include <cstdio>
#include <cstdlib>
typedef struct TreeNode *Tree;
struct TreeNode {
int v;
Tree Left;
Tree Right;
int flag;
};
Tree NewNode(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 ResetT(Tree T);
void FreeTree(Tree T);
int main()
{
int N, L;
Tree T;
//先把一棵树建好,然后判断的顺序读进去进行比较
//相同:标志为0,不相同,标志为1,直接打印
scanf("%d", &N);
while (N) {
scanf("%d", &L);
T = MakeTree(N);
for (int i = 0; i < L; i++) {
if (Judge(T, N))
printf("N0\n");
else
printf("Yes\n");
ResetT(T);
}
FreeTree(T);
scanf("%d", &N);//保证下一次循环正常进行。
}
return 0;
}
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)
T = NewNode(V);
else {
if (V > T->v)
T->Right = Insert(T->Right, V);
else if (V < T->v)
T->Left = Insert(T->Left, V);
}
return T;
}
Tree MakeTree(int N)
{
Tree T;
int V;
scanf("%d", &V);
T = NewNode(V);
//建树的过程。先结点,然后插入
for (int i = 0; i < N; i++) {
scanf("%d", &V);
T = Insert(T, V);
}
}
int check(Tree T, int 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;//注意这里是访问过一次的结点
}
else {
if (V == T->v) {
T->flag = 1;
return 1;
}
else
return 0;
}
}
//只建立一棵树进行判断,那么怎么读入需要判断的结点就是需要思考的了
int Judge(Tree T, int N)
{
int V, flag = 0;
scanf("%d", &V);
//如果直接return的话,这一行还没有读完,需要再处理
if (V != T->v)
flag = 1;
else
T->flag = 1;//表示已经读取过
for (int i = 1; i < N; i++) {
scanf("%d", &V);
if ((!flag) && (!check(T,V)))
flag = 1;//未读取+检查不相等
}
return flag;
}
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);
}
上述的代码一定是有问题,但是因为不是按照自己思路写的,所以基本上就看不出错误在哪里了。如果是自己动手,理清思路写一遍,(虽然也有可能错得没有头绪)但至少思路是清晰的。
比如:
#include <cstdio>
#include <cstdlib> //需要malloc
typedef struct TreeNode *Tree;
struct TreeNode {
int v;
Tree Left;
Tree Right;
int flag;//访问后置为1
};
Tree MakeTree(int N);
Tree NewNode(int V);//传入的是结点数据
Tree Insert(Tree T, int V);
int Judge(Tree T, int N);
int check(Tree T, int V);
void ZeroFlag(Tree T);
void FreeTree(Tree T);
int main()
{
int N;
scanf("%d", &N);
while (N) {
int L;
scanf("%d", &L);
Tree T = MakeTree(N);
for (int i = 0; i < L; i++) {
if (Judge(T, N))
printf("No\n");
else
printf("Yes\n");
ZeroFlag(T);
}
FreeTree(T);
scanf("%d", &N);
}
return 0;
}
Tree MakeTree(int N)
{
Tree T;
int V;
scanf("%d", &V);
T = NewNode(V);
for (int i = 1; i < N; i++) {
scanf("%d", &V);
T = Insert(T, V);
}
//最后返回T,相当于根结点的位置
return T;
}
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)
T = NewNode(V);
else {
if (V > T->v)
T->Right = Insert(T->Right, V);//传到左子树,如果左子树没有,就新建
else if (V < T->v)
T->Left = Insert(T->Left, V);
}
return T;
}
int Judge(Tree T, int N)
{
int V, flag = 0;
scanf("%d", &V);
if (V != T->v) flag = 1;//1表示不一致
else T->flag = 1;//表示该点已访问
for (int i = 1; i < N; i++) {
scanf("%d", &V);
if((!flag) && (!check(T, V)))
flag = 1;
}
return flag;
}
int check(Tree T, int V)
{
if (T->flag) {
//T结点数据已经访问,继续左子或右子
if (V > T->v)
return check(T->Right, V);
else if (V < T->v)
return check(T->Left, V);
else
return 0;
}
else {
if (V == T->v) {
T->flag = 1;
return 1;
}
else
return 0;
}
}
void ZeroFlag(Tree T)
{
if (T->Left)
ZeroFlag(T->Left);
if (T->Right)
ZeroFlag(T->Right);
T->flag = 0;
}
void FreeTree(Tree T)
{
if (T->Left)
FreeTree(T->Left);
if (T->Right)
FreeTree(T->Right);
free(T);
}
反思
这段代码完工之前有一个一直看不出来的问题:
void ZeroFlag(Tree T)
{
if (T->Left)
ZeroFlag(T->Left);
if (T->Right)
ZeroFlag(T->Right);
else T->flag = 0;
}
void FreeTree(Tree T)
{
if (T->Left)
FreeTree(T->Left);
if (T->Right)
FreeTree(T->Right);
else free(T);
}
这两个小函数,是通过递归来实现标志位清零和释放,所以可以通过if来判断,但是我习惯性地最后加上了else,问题就出来了。
这么来思考:
比如标志位清零吧,如果左子树存在,那么递归到左子树的根结点,然后再次判断,假如此结点就只有根,那么两个if都不成立,这时的else执行时没问题的。但是,当它返回的时候,else是不执行的啊!原因很简单,if不满足时,才执行else!
所以,这么写最后只能清掉叶子结点……
补充说明
这道题的全文描述:
是否同一棵二叉搜索树 (25 point(s))
给定一个插入序列就可以唯一确定一棵二叉搜索树。然而,一棵给定的二叉搜索树却可以由多种不同的插入序列得到。例如分别按照序列{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
说回来,也许一道简单的题,对于小白来说,瞪半都不知道咋回事,这时候不妨把之前写的全推翻,重新梳理,再写一次!。