(L3-016)二叉搜索树的结构(建树+离散化)

84 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

题目链接:PTA | 程序设计类实验辅助教学平台

image.png

输入样例: 5 2 4 1 3 0 8 2 is the root 1 and 4 are siblings 3 and 0 are on the same level 2 is the parent of 4 3 is the left child of 4 1 is the right child of 2 4 and 0 are on the same level 100 is the right child of 3 输出样例: Yes Yes Yes Yes Yes No No No 分析:先说这道题目思路,后说一些细节,这是一道思路不难细节巨多的一道题目。

分析:先看看题目中的询问,问某点是否为根,根肯定是第一个插入的数,所以这个询问比较容易处理,下一个是A和B是否为兄弟节点,这个我们可以在建树时加上一个记录父亲节点的数组,让每个点的父亲节点被记录下来,这样判断询问的时候直接判断两个节点的父亲节点是不是一个节点就可以了,而下面一个询问更为直接,直接询问父亲节点,所以直接用父亲节点数组判断即可。接下来两个询问分别是判断A是B的左孩子还是右孩子,这个我们在建树过程中是肯定要存下来的,因为我们建树是一个递归过程,而向下递归就是向左儿子或者右儿子递归实现的,所以左右孩子数组肯定是要记录下来的,最后一个是判断A和B是否在同一层上,这个我们可以写一个单独的函数进行搜索某个节点的层数,然后分别对两个节点调用函数直接判断即可。

上面就是这道题的思路了,思路是比较简单的,主要是一些细节方面的处理:

第一个:读入问题

问题的格式非常难处理,我一开始就真的直接一个字符一个字符来判断,这样会使读入变得极其复杂,其实在string里面有一个find函数,就是查找变量里面是否含有某个字串的,所以我们只需要找到这些询问之间的不同点然后直接进行逐个判断即可,读入的时候用getline直接读入一行,然后分别判断属于哪个询问即可。举个简单的例子:如果子串中含有root,那么一定是询问A是否为树的根,这个时候我们就可以用s.find("root")!=s.npos来进行判断,如果s串中不含有“root”这个字串就会返回s.npos,否则就会返回找到的“root”的位置(注意下标从0开始),这个功能还是非常强的,希望大家记住这个用法。

下面读入之后我们来看看如和把其中的数给处理出来,先说一种比较暴力的方法,就是说我们用for循环查找字符串中的数字的位置,然后逐个把数字取出,这里需要处理一下符号,就是有可能字符串中给出的两个整型含有负数,但是这个也比较好判断,就是我们找出来数字的开端,只要看一下这个数字前面一个字符是不是-即可,但是总的来说这样处理下来还是比较麻烦的,我在后面会附上这种解法的代码

下面来说一下用sscanf来简化处理数据的方法

对于固定的字符串,由于字符串中除了两个整型之外的格式都是已知的,所以我们可以对其进行格式化输入,也就是说我们先把询问读入s中,然后用sscanf格式读入整型即可。

还是以判断A是否是树的根为例:sscanf(s.c_str(),"%d is the root",&u);

这样就可以比较容易地取出字符串里面的整型,当然这种方法只适用于我们对字符串格式已知的情况

由于题目中所涉及的数都是在int范围内,其中一部分数我们是需要通过数组索引来访问的,但是我们不能开太大,所以需要对所给的数进行离散化,最后一个需要注意的细节就是题目中所涉及的询问中所给出的数不一定是树中已经存在的数,所以这种情况我们也需要考虑到,对于含有树中不存在的数的询问我们只需要输出No即可。

下面附上相应代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
const int N=1e7+10;
int t[N],idx,l[N],r[N],ll[N],rr[N];
bool vis[N];
int fu[N],a[N];
map<int,int>mp;
vector<int> alls;
int find(int x)
{
	return lower_bound(alls.begin(),alls.end(),x)-alls.begin()+1;
}
void build(int pos,int val)
{
	if(!vis[pos])
	{
		vis[pos]=true;
		t[pos]=val;
		return ;
	}
	if(val<t[pos])
	{
		if(l[pos])
			build(l[pos],val);
		else
			fu[val]=t[pos],ll[t[pos]]=val,l[pos]=++idx,build(l[pos],val);
	}
	else
	{
		if(r[pos])
			build(r[pos],val);
		else
			fu[val]=t[pos],rr[t[pos]]=val,r[pos]=++idx,build(r[pos],val);
	}
}
int findh(int pos,int x,int h)
{
	if(t[pos]==x) return h;
	if(x<t[pos]) return findh(l[pos],x,h+1);
	else return findh(r[pos],x,h+1);
}
int main()
{
	int n,m;
	idx=1;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int tt;
		scanf("%d",&a[i]);
		alls.push_back(a[i]);
		mp[a[i]]=1;
	}
	sort(alls.begin(),alls.end());
	alls.erase(unique(alls.begin(),alls.end()),alls.end());
	for(int i=1;i<=n;i++)
	{
		int tt=find(a[i]);
		build(1,tt);
	}
	cin>>m;
	getchar();
	string s;
	while(m--)
	{
		getline(cin,s);
		if(s.find("root")!=s.npos)
		{
			int u;
			sscanf(s.c_str(),"%d is the root",&u);
			if(find(u)==t[1]&&mp[u]) printf("Yes");
			else printf("No");
		}
		else if(s.find("siblings")!=s.npos)
		{
			int u,v;
			sscanf(s.c_str(),"%d and %d are siblings",&u,&v);
			if(fu[find(u)]==fu[find(v)]&&mp[u]&&mp[v]) printf("Yes");
			else printf("No");
		}
		else if(s.find("parent")!=s.npos)
		{
			int u,v;
			sscanf(s.c_str(),"%d is the parent of %d",&u,&v);
			if(find(u)==fu[find(v)]&&mp[u]&&mp[v]) printf("Yes");
			else printf("No");
		}
		else if(s.find("left")!=s.npos)
		{
			int u,v;
			sscanf(s.c_str(),"%d is the left child of %d",&u,&v);
			if(find(u)==ll[find(v)]&&mp[u]&&mp[v]) printf("Yes");
			else printf("No");
		}
		else if(s.find("right")!=s.npos)
		{
			int u,v;
			sscanf(s.c_str(),"%d is the right child of %d",&u,&v);
			if(find(u)==rr[find(v)]&&mp[u]&&mp[v]) printf("Yes");
			else printf("No");
		}
		else
		{
			int u,v;
			sscanf(s.c_str(),"%d and %d are on the same level",&u,&v);
			if(findh(1,find(u),1)==findh(1,find(v),1)&&mp[u]&&mp[v]) printf("Yes");
			else printf("No");
		}
		if(m) puts("");
	}
	return 0;
}