本次笔记内容:
小白专场:1.题意理解及搜索树表示
小白专场:2.程序框架及建树
小白专场:3.搜索树是否一样的判别
文章目录
题意理解
给定一个插入序列,就可以唯一确定一棵二叉搜索树。然而,一棵给定的二叉搜索树却可以由多种不同的插入序列得到。
- 例如,按照序列{2, 1, 3}和{2, 3, 1}插入初始为空的二叉搜索树,都得到一样的结果。
本题问题
对于输入的各种插入序列,你需要判断它们是否能生成一样的二叉搜索树。
输入输出样例
4 2 // 插入的结点个数为4,后面有2个序列需要比较
3 1 4 2 // 用于比较的序列
3 4 1 2 // 与样本比较的序列
3 2 4 1
2 1 // 2个结点,比较1组
2 1
1 2
0 // 取消输入
输出样例:
Yes
No
No
求解思路
分别建两棵搜索树的判别方法
根据两个序列分别建树,在判别树是否一样。
不建树的判别方法
如上图,将序列拆解为左子树序列+根+右子树序列,再对左右子树序列两两比较。
建一棵树,再判别其他序列是否与该树一致
- 搜索树表示
- 建搜索树T
- 判别一序列是否与搜索树T一致
搜索树表示
typedef struct TreeNode *Tree;
struct TreeNode
{
int v;
Tree Left, Right;
int flag;
};
flag用于记录该结点是否被访问过。
程序框架
int main() {
对每组数据 {
读入N和L
根据第一行序列建树T
依据树T分别判别后面的L个序列是否能与T形成同一搜索树并输出结果
}
return 0;
}
需要设计的主要函数:
- 读数据建搜索树T;
- 判别一序列是否与T构成一样的搜索树。
具体语句:
int main()
{
int N, L, i;
Tree T;
scanf("%d", &N);
while (N)
{
scanf("%d", &L);
T = MakeTree(N);
for (i = 0; i < L; i++)
{
if (Judge(T, N))
printf("Yes\n");
else
printf("Np\n");
ResetT(T); /* 清除T中的标记flag */
}
FreeTree(T);
scanf("%d", &N)
}
return 0;
}
建立函数
如何建立搜索树
Tree MakeTree(int N)
{
Tree T;
int i, V;
scanf("%d", &V);
T = NewNode(V);
for (i = 1; i < N; i++)
{
scanf("%d", &V);
T = Insert(T, V);
}
return T;
}
其中,NewNode(V)是建立一个根节点。
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
T->Left = Insert(T->Left, V);
}
return T;
}
判别函数
将问题转换为:按顺序在T上查找,查找时经过的结点以前没碰到过(即被比较的序列中还未被查找过),则断定不一致。
如上图,在T中查找2时,经历3->1->2,而现在只碰到过3、2,没有1,因此断定不一致。
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;
}
}
check()是对一个数的检查。
int Judge(Tree T, int N)
{ /* 有bug版本 */
int i, V;
scanf("%d", &V);
if (V != T->v)
return 0;
else
T->flag = 1;
for (i = 1; i < N; i++)
{
scanf("%d", &V);
if (!check(T, V))
return 0;
}
return 1;
}
Judge()是对一个序列的检查。
上述Judge存在bug,用户键入时将全部序列依次键入;如果检查到一半发现不一致就退出,则会把后半部分当成下一个序列的一部分。因此,程序必须把序列全部读完。
int Judge(Tree T, int N)
{
int i, V, flag = 0;
scanf("%d", &V);
if (V != T->v)
flag = 1;
else
T->flag = 1;
for (i = 1; i < N; i++)
{
scanf("%d", &V);
if ((!flag) && !check(T, V))
flag = 1;
}
if (flag)
return 0;
return 1;
}
如上,在Judge()函数内置局部变量flag,用于表示现在是否已经产生矛盾。
ResetT()与FreeTree()
void ResetT(Tree T)
{
/* 清除T中各结点的flag标记 */
if (T->Left)
ResetT(T->Left);
if (T->Right)
ResetT(T->Right);
T->flag = 0;
}
void FreeTree(Tree T)
{
/* 释放T的空间 */
if (T->Left)
FreeTree(T->Left);
if (T->Right)
FreeTree(T->Right);
free(T);
}