04-树4 是否同一棵二叉搜索树 (25 分)

题目来源:浙江大学数据结构MOOC配套习题(PTA)

给定一个插入序列就可以唯一确定一棵二叉搜索树。然而,一棵给定的二叉搜索树却可以由多种不同的插入序列得到。例如分别按照序列{2, 1, 3}和{2, 3, 1}插入初始为空的二叉搜索树,都得到一样的结果。于是对于输入的各种插入序列,你需要判断它们是否能生成一样的二叉搜索树。

输入格式:

输入包含若干组测试数据。每组数据的第1行给出两个正整数N (≤10)和L,分别是每个序列插入元素的个数和需要检查的序列个数。第2行给出N个以空格分隔的正整数,作为初始插入序列。最后L行,每行给出N个插入的元素,属于L个需要检查的序列。

简单起见,我们保证每个插入序列都是1到N的一个排列。当读到N为0时,标志输入结束,这组数据不要处理。

输出格式:

对每一组需要检查的序列,如果其生成的二叉搜索树跟对应的初始序列生成的一样,输出“Yes”,否则输出“No”。

输入样例:

1
2
3
4
5
6
7
8
4 2
3 1 4 2
3 4 1 2
3 2 4 1
2 1
2 1
1 2
0

输出样例:

1
2
3
Yes
No
No

题意

这个题目是给你一些建树用的数据,第一组树的数据作为判断的基准,后面的数据作为拿来判断是不是同一棵二叉搜索树的测试数据

思路

这道题有三种思路

  1. 不建树,根据数据大小分类序列,递归判断序列是否相一致;
  2. 建两棵树,根据序列建两棵树再判断两棵树是否一致;
  3. 建一棵树,把判断基准建立为一棵基准树,其它的数据放进来和这棵树比较

本文采用第三种方法——建立一棵树然后新的序列与这棵树作比较
代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include <stdio.h>
#include <stdlib.h>

typedef struct TreeNode *Tree;
struct TreeNode
{
Tree Left,Right;//用两个指针来指向左右子树
int data,flag;//data来存放这个节点的数据,flag用来判断是否访问过该节点
};

Tree BuildTree(int N);
Tree NewNode(int V);
Tree Insert(Tree T,int V);
char Judge(Tree T,int N);
char check(Tree T,int N);
void FreeTree(Tree T);
void ResetT(Tree T);

int main()
{
int N,i,L;
Tree T;
scanf("%d",&N);
while(N)//当N为0时返回
{
scanf("%d",&L);
T = BuildTree(N);//建立二叉搜索树
for(i = 0; i < L; i++)//多组数据,使用读入的L来控制读入的数据组数
{
if(Judge(T,N)) printf("Yes\n");//判断是否是一棵树
else printf("No\n");
ResetT(T);//将T初始化
}
FreeTree(T);//判断完成后释放当前树
scanf("%d",&N);
}
return 0;
}

Tree BuildTree(int N)
{
int i,V;
Tree T;
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 -> data = V;
T -> Left = T->Right = NULL;
T -> flag = 0;//初始化操作
return T;
}

Tree Insert(Tree T,int V)
{
if(!T) T = NewNode(V);//查找到空节点时开辟新节点并插入
else{
if(T->data > V)//此节点数据大于要插入的数据时向左插入
T->Left = Insert(T->Left,V);
else
T->Right = Insert(T->Right,V);
}
return T;
}
char Judge(Tree T,int N)
{
int i,V,Jflag = 0;//使用Jflag来保证在中途判断出不是同一棵树后仍能继续读数据和判断
scanf("%d",&V);
if( V != T->data) Jflag = 1;
else T->flag = 1;
for(i=1;i<N;i++)
{
scanf("%d",&V);
if((!check(T,V)) && (!Jflag)) Jflag = 1;
}
if(Jflag) return 0;
else return 1;
}

char check(Tree T,int V)
{
if(T->flag)//当这个节点被访问过
{
//要判断的数据和这个节点不等时根据大小关系向左向右判断
if(T->data>V) return check(T->Left,V);
else if(T->data<V) return check(T->Right,V);
else return 0;//如果这个节点被访问过且数据和要判断的数据相同说明不是
}
else{
if(V == T->data){//未访问过该节点且该节点数据和需要判断的数据相等
T->flag = 1;
return 1;
}
else return 0;
}
}

void FreeTree(Tree T)
{//释放判断的基准树
if(T->Right) FreeTree(T->Right);
if(T->Left) FreeTree(T->Left);
free(T);
}

void ResetT(Tree T)
{//判断完成后的树的初始化
if(T->Right) ResetT(T->Right);
if(T->Left) ResetT(T->Left);
T->flag = 0;
}