目录
题意理解
问题
描述
输入样例
输出样例
求解思路
建两棵二叉树
不建树
建一棵树
搜索树表示
程序框架搭建
如何建搜索树
如何判别
方法
查找函数
判断函数
其他函数
题意理解
给定一个插入序列就可以唯一确定一颗二叉搜索树。
但是,一颗给定的二叉搜索树却可以由多种不同的插入序列得到。
例如,按照序列{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
1 2
0
输出样例
Yes
No
No
求解思路
建两棵二叉树
根据两个序列分别建树,再去递归判别两棵树是否一样。
不建树
例如:
3 1 2 4 对比 3 4 1 2,3都是根结点。
那找出所有比3大的结点和所有比3小的结点,发现:
很显然,我们能看出他们是同一棵二叉搜索树了。
再来对比一组:3 1 2 4、3 2 4 1。
这一组序列并不是同一棵二叉搜索树。
建一棵树
根据结点数来建立一棵二叉搜索树T,再来判别一个序列是否与二叉搜索树T一致。
分为三部分:
- 搜索树表示
- 建立搜索树T
- 判别一序列是否与搜索树T一致
搜索树表示
typedef struct TreeNode* Tree;
struct TreeNode
{
int v;
Tree Left, Right;
int flag;
};
程序框架搭建
int main()
{
对每组数据
1.读入N和L
2.根据第一行序列建树T
3.依据树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("No\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;
}
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;
}
如何判别
如何判别序列3 2 4 1是否与树T一致呢?
方法
在树T中按顺序搜索序列3 2 4 1中的每个数:
- 如果每次搜索所经过的结点在前面均出现过,则一致
- 否则(某次搜索中遇到前面未出现过的结点),则不一致
查找函数
check函数接受两个参数:一个二叉搜索树T和一个整数V。
函数的返回值为整型。
在二叉搜索树T中查找整数V, 如果找到了,则将该结点的标记flag设为1,并返回1;
如果没有找到,则返回0。
用递归的方式:首先判断当前结点是否被标记,如果被标记,则根据V与当前结点的大小关系,递归地查找左子树或右子树。
如果当前结点未被标记,则判断V是否等于当前结点的值, 如果等于,则将该结点的标记设为1,并返回1;
否则返回0。
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 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;
}
函数有两个参数:一个是指向树的指针T,另一个是整数N。
首先从输入中读取一个整数V,如果V不等于树T的根结点值,则返回0, 表示输入的第一个数与树T不匹配。
如果V等于树T的根结点值,则将根结点的标记flag设置为1, 表示该节点已经被访问过。
接下来,用一个循环来读取剩余的N-1个整数。
对于每个整数V,函数调用check函数来检查树T中是否存在一个结点的值等于V。
如果不存在这样的节点,则返回0,表示输入的整数与树T不匹配。
如果存在这样的节点,则将该节点的标记flag设置为1, 表示该节点已经被访问过。
最后,如果所有的输入整数都能在树T中找到对应的节点,则返回1,表示输入的整数与树T匹配。
但是这段代码是存在bug的,当发现序列中的某个数与T不一致时,返回0程序结束,而不会把后面的数读完了。
那么就会误把后面的数当成下一组序列来看了,导致序列错乱了。
所以,当发现序列中的某个数与T不一致时,必须把序列后面的数都读完。
据此来改进代码:
int Judge(Tree T, int N)
{
int i, V, flag = 0;
/*flag:0代表目前还一致,1代表已经不一致*/
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;
else
return 1;
}
输入参数为一个指向二叉树根结点的指针T和一个整数N,表示序列的长度。
函数的返回值为1或0,表示给定的二叉树是否与序列匹配。
首先从输入中读取第一个整数V,表示序列中的第一个元素。
如果V与二叉树根结点的值不相等,则将flag(这里是函数内部的flag标记)标记为1,表示当前已经不一致了;
否则将根节点的flag(这里是结点内的flag标记,表示是否访问过)标记为1,表示已经匹配上了。
再循环读取序列中的剩余元素,每次读取一个整数V。
如果当前已经不一致了(即flag为1),则直接跳过后面的元素。
如果当前还一致,并且当前节点的值等于V,则将当前节点的flag标记为1,表示已经匹配上了;
否则将flag标记为1,表示当前已经不一致了。
循环结束后,如果flag为1,则说明序列与二叉树不匹配,返回0;
否则返回1,表示匹配成功。
其他函数
清除T中各结点的flag标记
void ResetT(Tree T) /*清除T中各结点的flag标记*/
{
if (T->Left)
ResetT(T->Left);
if (T->Right)
ResetT(T->Right);
T->flag = 0;
}
先递归处理其左子树,再递归处理其右子树, 最后将该结点的flag标记设为0,表示清除标记。 这样,整个二叉树中所有节点的flag标记都被清除了。
释放T的空间
void FreeTree(Tree T) /*释放T的空间*/
{
if (T->Left)
FreeTree(T->Left);
if (T->Right)
FreeTree(T->Right);
free(T);
}
end
学习自:MOOC数据结构——陈越、何钦铭