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

博客围绕判断插入序列能否生成相同二叉搜索树展开。介绍了输入输出格式及样例,指出给定插入序列可确定二叉搜索树,但同一树可有不同插入序列。还提及题目有建树和不建树两种解法,作者用建树方法,通过标记节点判断是否为同一树,并结合样例说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给定一个插入序列就可以唯一确定一棵二叉搜索树。然而,一棵给定的二叉搜索树却可以由多种不同的插入序列得到。例如分别按照序列{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

鸣谢青岛大学周强老师补充测试数据!

题目分析:

这个题在mooc上何老师介绍有两种方法可以写,一种建树,一种不建树。但是我用不建树的方法没拿到满分,有一个评测点没过。所以这道题我介绍下建树怎么去做。在一个给定的二叉搜索树上,如果再输入一个树比较,其实可以用标记的方法,因为如果你插入的这个数字是在给定的二叉树上,那么你访问它的话就一定要经过前面的每个顶点。如果前面没有访问过,说明它本来应该插入的是那个位置,而不是其它位置。可能有点不太好理解,这里用样例举个例子:先用给定的序列3142建立二叉搜索树,然后3根节点相同,标记为1.接下来是4,查询4的时候,访问了已经标记过的节点3,没有问题,把4标记为1.再然后是1,也是访问了3,没问题,将1标记为1.最后是2,查找2经过了3,1,都被标记了,没问题。所以这个是同一个二叉搜索树。再看第二个序列,3241. 根节点3相同,将3标记为1,然后是2,发现访问2经过了未被访问的节点1,说明2在这个二叉树当中应该在1的位置。所以它不是同一个二叉搜索树。下面再通过代码和注释更好的理解一下。

代码如下:

#include <iostream>
#include <new>

using namespace std;
typedef struct TreeNode* Tree;
struct TreeNode{
	int x;
	Tree left, right;
	int flag;   //这个标记用于在二叉搜索树中找对应元素看他是不是前面经过的每个节点都被访问了
};    //因为没有访问的话说明要查询的节点应该插在这个位置,而不是其它位置。

Tree CreatTree(int n);    //建树
Tree Insert(Tree T, int v);   //判断插入节点到二叉搜索树当中
Tree NewNode(int v);   //插入节点
void DestoryTree(Tree T);  //销毁树
void ResertTree(Tree T);  //释放标记
bool Judge(Tree T, int n);  //判断是不是同一个二叉树
bool Check(Tree T, int v);  //判断每个节点的标记

int main()
{
    int n, i, m;
    Tree T;
    cin >> n;
    while( n ) {
		cin >> m;
		T = CreatTree(n);
		for( i=0; i<m; i++ ) {
			if( Judge(T, n) ) {
				cout << "Yes" << endl;
			} else {
				cout << "No" << endl;
			}
			ResertTree(T);
		}
		DestoryTree(T);
		cin >> n;
    }
    return 0;
}

Tree CreatTree(int n)
{
	int i, v;
	Tree T;
	cin >> v;
	T = NewNode(v);
	for( i=1; i<n; i++ ) {
		cin >> v;
		T = Insert(T, v);
	}

	return T;
}

Tree NewNode(int v)
{
	Tree T = new (struct TreeNode);
	T->flag = 0;
	T->left = T->right = NULL;
	T->x = v;

	return T;
}

Tree Insert(Tree T, int v)
{
	if( T == NULL ) {
		T = NewNode(v);
	}
	else {    //判断应该插入的位置
		if( v < T->x ) {
			T->left = Insert(T->left, v);
		} else {
			T->right = Insert(T->right, v);
		}
	}

	return T;
}

bool Judge(Tree T, int n)
{
	int v, flag = 1, i;

	cin >> v;
	if( v != T->x )   //先判断根节点
		flag = 0;
	else
		T->flag = 1;
	for( i=1; i<n; i++ ) {   //依次查询后续每个元素
		cin >> v;
		if( flag && !Check(T, v) ) {
			flag = 0;
		}
	}

	return flag;
}

bool Check(Tree T, int v)   //判断查询的这个元素,再它之前的元素是不是都被访问过了
{
	if( T->flag ) {   
		if( v <= T->x )
			return Check(T->left, v);
		else if( v > T->x )
			return Check(T->right, v);  
	}
	else {
		if( v == T->x ) {
			T->flag = 1;
			return true;
		}
		else
			return false;
	}
}

void ResertTree(Tree T)
{
	if( T->left )
		ResertTree(T->left);
	if( T->right )
		ResertTree(T->right);
	T->flag = 0;
}

void DestoryTree(Tree T)
{
	if( T->left )
		DestoryTree(T->left);
	if( T->right )
		DestoryTree(T->right);
	delete T;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值