山东理工大学2021年全国天梯赛赛前个人专题强化赛---5(并查集、搜索树)补题

8 篇文章 1 订阅
2 篇文章 0 订阅

社交集群

在这里插入图片描述
思路: 利用并查集把相同兴趣的人放在一个集合中,最后求有几个集合,最后还要降序输出集合中的人数

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int fa[N];
bool cmp(int a,int b){
    return a>b;
}
int find(int x){
	if(x!=fa[x]) fa[x]=find(fa[x]);
	return fa[x];
}
void Union(int x,int y){
	if(find(x)!=find(y)){
		fa[find(x)]=find(y);
	}
}
int p[N];
int a[2000];
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
	for(int i=1;i<=n;i++){
		int m,k;
		char op[2];
		scanf("%d",&m);
		scanf("%s",op);
		for(int j=0;j<m;j++){
			scanf("%d",&k);
            if(p[k]==0){
            	p[k]=i;  //就是记录第i个人的兴趣分布, 
			}
			else{
				Union(p[k],i); //兴趣k前面已经有人有过了,这时就要合并这两个人在一个集合 
			}
        }
	}

    int sum=0;
    int a[2000]={0};
    int x=0;
    for(int i=1;i<=n;i++){
        if(fa[i]==i){   //求不同的社交集群 
            sum++;
        }
        a[find(i)]++;   //a存的是集群中的人数,就是每个并查集合中有多少人 
    }
    printf("%d\n",sum);
    sort(a,a+1001,cmp);      //降序输出 
    for(int i=0;i<sum;i++){
        if(i==0) printf("%d",a[i]);
        else printf(" %d",a[i]);
    }
    printf("\n");
    return 0;
}

是否同一棵二叉搜索树

在这里插入图片描述
思路: 就是建两个二叉搜索树,再进行比较。关键就是建树和比较函数怎么写。

#include<bits/stdc++.h>
using namespace std;
struct node{
	struct node *l,*r;
	int data;
};
struct node *creat(struct node *root,int x){
	if(root==NULL){
		root=(struct node*)malloc(sizeof(struct node));
		root->data=x;
		root->l=NULL;
		root->r=NULL;
	}
	else if(x<root->data){
		root->l=creat(root->l,x);
	}
	else{
		root->r=creat(root->r,x);
	}
	return root;
}
int compare(struct node *root1,struct node *root2) //进行比较,当前节点相同时,就比较左右儿子节点,依次递归,直到到最后 
 {
 	if(root1==NULL&&root2==NULL) return 1; //同时为空结点,说明一样 
 	else if(root1&&root2) //同时不为空结点,就开始比较数据,相同则比较左右儿子,左右儿子必须完全相同 
 	{
 		if(root1->data==root2->data) return (compare(root1->l,root2->l)&&compare(root1->r,root2->r));
 		else return 0; //否则就为0 
	 }
	else return 0;
 }
 void pre(struct node *root){
 	if(root==NULL) return ;
 	else{
 		printf("%d ",root->data);
 		pre(root->l);
 		pre(root->r);
	 }
 }
int main(){
	int n,m;
	while(~scanf("%d",&n)&&n!=0){
		scanf("%d",&m);
		struct node *root1;
		root1=NULL;
		for(int i=0;i<n;i++){
			int x;
			scanf("%d",&x);
			root1=creat(root1,x);
		}
		
		struct node *root2;
		for(int i=0;i<m;i++){
			root2=NULL;
			for(int j=0;j<n;j++){
				int x;
				scanf("%d",&x);
				root2=creat(root2,x);
			}
			
			if(compare(root1,root2)){
				printf("Yes\n");
			}
			else{
				printf("No\n");
			}
		}
	}
	return 0;
}

朋友圈

在这里插入图片描述
思路: 就是求所有朋友圈中人数最大的数量,先根据输入建立并查集,再求每个并查集中的人数。
这里要注意的一点就是 在求人数的时候,要先维护一下并查集,让每个结点都指向根节点。(我就是一开始没注意这点,导致卡了一段时间),最后没找到一个根节点,看有几个点只想根节点就行,求出最大值。
如果不维护的话,就看结点的父亲或者节点的爷爷或者结点的祖父是否指向根节点,并计数。

下面是不维护版本

for(int i=1;i<=n;i++){
		if(fa[i]==i){
			for(int j=1;j<=n;j++){
				if(fa[j]==i||fa[fa[j]]==i||fa[fa[fa[j]]]==i){
					s[i]++;
				}
			}
		}
	}

下面是维护版本

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int fa[N];
int find(int x){
    if(x!=fa[x]) fa[x]=find(fa[x]);
    return fa[x];
}
void build(int a,int b){
     if(find(a)!=find(b)){
         fa[find(a)]=find(b);
     }
}
int s[N];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
	for(int i=0;i<m;i++){
		int t,k;
		cin>>t;
		int pre;
		for(int j=0;j<t;j++){
			cin>>k;
			if(j==0){
				pre=k; //记下第一个人 
			}
			else{
				build(k,pre); //当前与上一个放在一个集合 
			}
		}
	}
	for(int i=1;i<=n;i++){ //维护 
		find(i);
	}
	int sum=0;
	int ans=0;
	for(int i=1;i<=n;i++){
		if(fa[i]==i){
			for(int j=1;j<=n;j++){
				if(fa[j]==i){
					s[i]++;
				}
			}
		}
	}
	for(int i=1;i<=n;i++){
	
		ans=max(ans,s[i]);
	}
	printf("%d\n",ans);
	return 0;
}

树种统计

随着卫星成像技术的应用,自然资源研究机构可以识别每一棵树的种类。请编写程序帮助研究人员统计每种树的数量,计算每种树占总数的百分比。

输入格式:
输入首先给出正整数N(≤10
​5
​​ ),随后N行,每行给出卫星观测到的一棵树的种类名称。种类名称由不超过30个英文字母和空格组成(大小写不区分)。

输出格式:
按字典序递增输出各种树的种类名称及其所占总数的百分比,其间以空格分隔,保留小数点后4位。

输入样例:
29
Red Alder
Ash
Aspen
Basswood
Ash
Beech
Yellow Birch
Ash
Cherry
Cottonwood
Ash
Cypress
Red Elm
Gum
Hackberry
White Oak
Hickory
Pecan
Hard Maple
White Oak
Soft Maple
Red Oak
Red Oak
White Oak
Poplan
Sassafras
Sycamore
Black Walnut
Willow
输出样例:
Ash 13.7931%
Aspen 3.4483%
Basswood 3.4483%
Beech 3.4483%
Black Walnut 3.4483%
Cherry 3.4483%
Cottonwood 3.4483%
Cypress 3.4483%
Gum 3.4483%
Hackberry 3.4483%
Hard Maple 3.4483%
Hickory 3.4483%
Pecan 3.4483%
Poplan 3.4483%
Red Alder 3.4483%
Red Elm 3.4483%
Red Oak 6.8966%
Sassafras 3.4483%
Soft Maple 3.4483%
Sycamore 3.4483%
White Oak 10.3448%
Willow 3.4483%
Yellow Birch 3.4483%
思路: 利用map,把每个字符串出现的频率记下来,最后按照升序输出,注意输出格式就行,还有百分号的输出方法
注意 :用getline(cin,s)时,要注意第一个回车会被读入的问题,用个getchar(),或者一开始就用char来存,然后用getline(s,50)

#include<iostream>
#include<cstring>
#include<map>
#include<algorithm>
using namespace std;
map<string,int> M;
int n;
int main(){
	scanf("%d",&n);
	string s;
	//getchar();//防止回车被getline读入,或者用getline(s,50),s用char定义 
	for(int i=0;i<n;i++){
		getline(cin,s);   
		M[s]++;
	} 
	for(map<string,int>::iterator it=M.begin();it!=M.end();it++){
		cout<<it->first<<" ";
		printf("%.4lf%%\n",(double)it->second/n*100);
	}
}

搜索树判断

对于二叉搜索树,我们规定任一结点的左子树仅包含严格小于该结点的键值,而其右子树包含大于或等于该结点的键值。如果我们交换每个节点的左子树和右子树,得到的树叫做镜像二叉搜索树。

现在我们给出一个整数键值序列,请编写程序判断该序列是否为某棵二叉搜索树或某镜像二叉搜索树的前序遍历序列,如果是,则输出对应二叉树的后序遍历序列。

输入格式:
输入的第一行包含一个正整数N(≤1000),第二行包含N个整数,为给出的整数键值序列,数字间以空格分隔。

输出格式:
输出的第一行首先给出判断结果,如果输入的序列是某棵二叉搜索树或某镜像二叉搜索树的前序遍历序列,则输出YES,否侧输出NO。如果判断结果是YES,下一行输出对应二叉树的后序遍历序列。数字间以空格分隔,但行尾不能有多余的空格。

输入样例1:
7
8 6 5 7 10 8 11
输出样例1:
YES
5 7 6 8 11 10 8
输入样例2:
7
8 6 8 5 10 9 11
输出样例2:
NO
思路: 就是先建立搜索树,然后把前序遍历和镜像前序遍历的结果分别用数组存下来,然后把两种结果分别与一开始搜索树,进行比较,只要有其中一个符合,就输出“YES”,然后输出后序遍历。
重点就是镜像二叉树的前序遍历,其实就是把原本的前序遍历“根左右”,变成了“根右左”,符合镜像的定义(把左右子树调换顺序)。
镜像二叉树的后序遍历,也就是把左右调换
最后再注意输出格式就行

#include<bits/stdc++.h>
using namespace std;
struct node{
	node *l,*r;
	int data;
};
const int N=2000;
int tree1[N],tree2[N],tree[N];
int cnt1,cnt2,f1=1,f2=1,f3,f4;
node *creat(node *root,int x){
	if(root==NULL){
		root=(struct node*)malloc(sizeof(struct node));
		root->data=x;
		root->l=NULL;
		root->r=NULL;
	} else{
		if(root->data>x) 
		   root->l=creat(root->l,x);
		else 
		   root->r=creat(root->r,x);   
	}
	return root;
}
void pre1(node *root) { //前序遍历 
	if(root){
		tree1[cnt1++]=root->data;
		pre1(root->l);
		pre1(root->r);
	}
}
void pre2(node *root){ //镜像的前序遍历 
 	if(root){
 		tree2[cnt2++]=root->data;
 		pre2(root->r);
 		pre2(root->l);
	 }
}
void postorder1(node *root){ //后序遍历 
	if(root){
		postorder1(root->l);
		postorder1(root->r);
		if(f3==0){       //输出格式要求 
			printf("%d",root->data);
			f3=1;
		} else{
			printf(" %d",root->data);
		}
	}
}
void postorder2(node *root){  //镜像数的后序遍历 
	if(root){
		postorder2(root->r);
		postorder2(root->l);
		if(f4==0){
			printf("%d",root->data);
			f4=1;
		} else{
			printf(" %d",root->data);
		}
	}
}
int main(){
	int n;
	scanf("%d",&n);
	node *root=NULL;
	for(int i=0;i<n;i++){
		scanf("%d",&tree[i]);
		root=creat(root,tree[i]);
	}
	pre1(root);
	pre2(root);
	for(int i=0;i<n;i++){  //前序的比较 
		if(tree[i]!=tree1[i]){
			f1=0;
			break;
		}
	}
	for(int i=0;i<n;i++){ //镜像的前序比较 
		if(tree[i]!=tree2[i]){
			f2=0;
			break;
		}
	}
	if(f1){
		puts("YES");
		postorder1(root);
	} else
	if(f2){
		puts("YES");
		postorder2(root);
	}
	else puts("NO");
	return 0;
}

是否完全二叉搜索树

在这里插入图片描述
思路: 建一个搜索树,再判断是否为完全二叉树就行。主要是完全二叉树的判断,最后再层序输出即可。
完全二叉树的判断: 除了最后一层,必须是满结点(和满二叉树一样),最后一层必须是从左往右排列。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue> 
using namespace std;
#define ll long long
typedef struct node{
      int data;
	  struct node *l,*r;  
}*ptree;
void build(ptree &root,int x){
	if(root==NULL) {
		root=new node();
		root->data=x;
		return ;
	}
	if(root->data>x) build(root->r,x);
	else build(root->l,x);
}
bool bfs(ptree root){
	queue<ptree> q;
	q.push(root);
	int have=0;
	bool f1=0; // 表示是否为叶子节点 
	bool f2=1;  // 是否为完全二叉树 
	while(!q.empty()){
		ptree now =q.front();
		q.pop();
		if(f1){ //判断是否为叶子节点,只要有儿子,就不是 
			if(now->l||now->r) f2=0;
		}
		if(now->r&&!now->l) f2=0; //有右无左,不满足定义 
		if(now->l&&!now->r) f1=1; //有左无右,说明后面的必须是叶子结点,否则就不是完全二叉树 
		if(!now->l&&!now->r) f1=1; //没左没右,说明是叶子节点,那后面的必须也是叶子节点 
		if(have++) putchar(' ');
		printf("%d",now->data);
		if(now->l) q.push(now->l);
		if(now->r) q.push(now->r);
	}
	return f2;
}
int main(){
	int n;
	scanf("%d",&n);
	ptree root=NULL;
	for(int i=1;i<=n;i++){
		int t;
		scanf("%d",&t);
		build(root,t);
	}
	if(bfs(root)) printf("\nYES");
	else printf("\nNO");
	return 0; 
}

笛卡尔树

笛卡尔树是一种特殊的二叉树,其结点包含两个关键字K1和K2。首先笛卡尔树是关于K1的二叉搜索树,即结点左子树的所有K1值都比该结点的K1值小,右子树则大。其次所有结点的K2关键字满足优先队列(不妨设为最小堆)的顺序要求,即该结点的K2值比其子树中所有结点的K2值小。给定一棵二叉树,请判断该树是否笛卡尔树。

输入格式:
输入首先给出正整数N(≤1000),为树中结点的个数。随后N行,每行给出一个结点的信息,包括:结点的K1值、K2值、左孩子结点编号、右孩子结点编号。设结点从0~(N-1)顺序编号。若某结点不存在孩子结点,则该位置给出−1。

输出格式:
输出YES如果该树是一棵笛卡尔树;否则输出NO。

输入样例1:
6
8 27 5 1
9 40 -1 -1
10 20 0 3
12 21 -1 4
15 22 -1 -1
5 35 -1 -1
输出样例1:
YES
输入样例2:
6
8 27 5 1
9 40 -1 -1
10 20 0 3
12 11 -1 4
15 22 -1 -1
50 35 -1 -1
输出样例2:
NO
思路: 对于搜索树的判断,利用中序遍历,把中序遍历的结果放在一个数组中,因为中序遍历是“左根右”,所以根据搜索树的左小右大的性质,其中序遍历一定是递增的,所以只要出现一个数变小了,就是"NO"。
对于最小堆的判断,就是每次都把父亲节点与左右儿子结点,最小堆,父亲节点一定小于左右儿子,只要有一个不符合,就“NO”
最后一个细节,就是用结构体建树,如何找根?只要把所有儿子标记出来,不是儿子的就是根。

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
struct node{
	int k1,k2;
	int l,r;
}a[N];
int root,flag=1;
int b[N];
int mid[N],cnt;  
void mid_order(int root){  //中序遍历 
	if(root!=-1){
		mid_order(a[root].l);
		mid[cnt++]=a[root].k1;
		mid_order(a[root].r);
	}
}
void min_heap(int root){ //小顶堆的判断 
	int l,r;
	if(a[root].l!=-1){
		l=a[root].l;
		if(a[l].k2<a[root].k2){ //如果父亲比儿子的结点还大(在有儿子的情况),就不符合定义 
			flag=0;
			return ;
		}
		min_heap(l);
	}
	if(a[root].r!=-1){ //与上面一样 
		r=a[root].r;
		if(a[r].k2<a[root].k2){
			flag=0;
			return ;
		}
		min_heap(r);
	}
}
int main(){
	int n;
	scanf("%d",&n);

	for(int i=0;i<n;i++){
		scanf("%d%d%d%d",&a[i].k1,&a[i].k2,&a[i].l,&a[i].r);
		if(a[i].l!=-1){  //把左右儿子不是-1的标记一下,方便下面找根 
			b[a[i].l]=1;
		} 
		if(a[i].r!=-1){ //把是儿子的标记出来,只有一个根是非儿子的,为0 
			b[a[i].r]=1;
		}
	}
	for(int i=0;i<n;i++){  //找根 
		if(b[i]==0){
			root=i;
			break;
		}
	}
	mid_order(root);
	for(int i=1;i<cnt;i++){
		if(mid[i]<=mid[i-1]){ //搜索树是左大右下,所以中序遍历的是递增的 
			flag=0;           //如果有一个数变小了就不符合搜索树定义 
			break;
		}
	}
	min_heap(root);
	if(flag) puts("YES");
	else puts("NO");
	return 0;
}

二叉搜索树的结构

二叉搜索树或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉搜索树。(摘自百度百科)

给定一系列互不相等的整数,将它们顺次插入一棵初始为空的二叉搜索树,然后对结果树的结构进行描述。你需要能判断给定的描述是否正确。例如将{ 2 4 1 3 0 }插入后,得到一棵二叉搜索树,则陈述句如“2是树的根”、“1和4是兄弟结点”、“3和0在同一层上”(指自顶向下的深度相同)、“2是4的双亲结点”、“3是4的左孩子”都是正确的;而“4是2的左孩子”、“1和3是兄弟结点”都是不正确的。

输入格式:
输入在第一行给出一个正整数N(≤100),随后一行给出N个互不相同的整数,数字间以空格分隔,要求将之顺次插入一棵初始为空的二叉搜索树。之后给出一个正整数M(≤100),随后M行,每行给出一句待判断的陈述句。陈述句有以下6种:

A is the root,即"A是树的根";
A and B are siblings,即"A和B是兄弟结点";
A is the parent of B,即"A是B的双亲结点";
A is the left child of B,即"A是B的左孩子";
A is the right child of B,即"A是B的右孩子";
A and B are on the same level,即"A和B在同一层上"。
题目保证所有给定的整数都在整型范围内。

输出格式:
对每句陈述,如果正确则输出Yes,否则输出No,每句占一行。

输入样例:
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

#include<bits/stdc++.h>
using namespace std;
struct node
{
    int data;
    struct node *l,*r;
}; //方便建立二叉搜索树的,不用于查询 
struct point{
	int fa,depth=-1;
}; //记录父亲结点,和深度,一开始深度初始化为-1,这样不在树上的结点就与在树上的区分出来了 
map<int,point> q;
struct node* tree(struct node *p,int x,int fa,int level)
{
    if(p==NULL)
    {
        p=(struct node*)malloc(sizeof(struct node));
        p->data=x;
        p->l=p->r=NULL;
        if(fa!=-1)
        {
            q[x].fa=fa; //x的父亲就是传进来的fa 
            q[x].depth=level; //深度 
        }
    }
    else
    {
        if(p->data>x)
        {
            p->l=tree(p->l,x,p->data,level+1); //递归的话,父亲就是比较的这个数,深度+1 
        }
        else if(p->data<x)
        {
            p->r=tree(p->r,x,p->data,level+1);
        }
    }
    return p;
};

int main()
{
    int n,i,x,m,y;
    struct node*root=NULL;
    cin>>n;
    int r;
    for(i=1; i<=n; i++)
    {
        scanf("%d",&x);
        if(i==1)
            r=x; //根结点 
        root=tree(root,x,-1,0);
    }
    q[r].fa=-1;  //根没有父亲 
    q[r].depth=0; //根的深度 
    cin>>m;
    cin.get(); //这是把回车吃掉 
    string s;
    int f=0;
    while(m--)
    {
        y=-1;
        f=0;
        getline(cin,s);
        if(s.find("root")!=string::npos) //找特定的字符串 
        {
            sscanf(s.c_str(),"%d is the root",&x); //输出特定的字符串,下同 
            if(x==r)     //根是之前标记的r就行 
            {
                f=1;
            }
        }
        else if(s.find("siblings")!=string::npos)
        {
            sscanf(s.c_str(),"%d and %d are siblings",&x,&y); //父亲一样就是兄弟 
            if(q[x].fa==q[y].fa)
            {
                f=1;
            }
        }
        else if(s.find("level")!=string::npos) //深度一样,同层 
        {
            sscanf(s.c_str(),"%d and %d are on the same level",&x,&y);
            if(q[x].depth==q[y].depth)
            {
                f=1;
            }
        }
        else if(s.find("parent")!=string::npos) //x是y的父亲 
        {
            sscanf(s.c_str(),"%d is the parent of %d",&x,&y);
            if(q[y].fa==x)
            {
                f=1;
            }
 
        }
        else if(s.find("left")!=string::npos) //x是y的左儿子,且父亲要比左儿子大 
        {
            sscanf(s.c_str(),"%d is the left child of %d",&x,&y);
            if(q[x].fa==y&&x<y)
            {
                f=1;
            }
        }
        else if(s.find("right")!=string::npos)
        {
            sscanf(s.c_str(),"%d is the right child of %d",&x,&y);
            if(q[x].fa==y&&x>y)
            {
                f=1;
            }
        }
        if(q[x].depth==-1||q[y].depth==-1&&y!=-1) f=0; //这是有些可能符合要求点但是不在树中,且y不能是根 
        if(f) cout<<"Yes\n";
        else cout<<"No\n";
 
    }
    return 0;
}

二叉搜索树的最近公共祖先

给定一棵二叉搜索树的先序遍历序列,要求你找出任意两结点的最近公共祖先结点(简称 LCA)。

输入格式
输入的第一行给出两个正整数:待查询的结点对数 M(≤ 1 000)和二叉搜索树中结点个数 N(≤ 10 000)。随后一行给出 N 个不同的整数,为二叉搜索树的先序遍历序列。最后 M 行,每行给出一对整数键值 U 和 V。所有键值都在整型int范围内。

输出格式:
对每一对给定的 U 和 V,如果找到 A 是它们的最近公共祖先结点的键值,则在一行中输出 LCA of U and V is A.。但如果 U 和 V 中的一个结点是另一个结点的祖先,则在一行中输出 X is an ancestor of Y.,其中 X 是那个祖先结点的键值,Y 是另一个键值。如果 二叉搜索树中找不到以 U 或 V 为键值的结点,则输出 ERROR: U is not found. 或者 ERROR: V is not found.,或者 ERROR: U and V are not found.。

输入样例:
6 8
6 3 1 2 5 4 8 7
2 5
8 7
1 9
12 -3
0 8
99 99
输出样例:
LCA of 2 and 5 is 3.
8 is an ancestor of 7.
ERROR: 9 is not found.
ERROR: 12 and -3 are not found.
ERROR: 0 is not found.
ERROR: 99 and 99 are not found.
思路: 对于二叉树如何还原,要先通过二叉搜索树的先序遍历序列把中序遍历构造出来,因为二叉搜索树特点是“左小右大”,所以可以通过二叉搜索树中序遍历是单调递增的特点,来构造出中序遍历。只要把二叉搜索树的先序遍历序列用sort排个序,就出来中序遍历了。
对于共同的祖先节点,可以利用二叉搜索树“父亲结点比左儿子大,父亲结点比右儿子小”的特点,用递归来找祖先。
如果当前结点在询问的u和y之间,那就是共同祖先。
如果当前节点比u和v都大,说明要找祖先在当前结点的左子树(因为左子树比当前结点小,两个结点都比当前点小,祖先那必然是满足在u和v之间,所以要递归找当前结点的左儿子)
如果当前节点比u和v都小,说明要找祖先在当前结点的右子树,理由同上。
对于有没有查询的点,就利用map把结点都记下来,查询的时候,发现在map里面没有,就是“ERROR”。
对于谁是谁的祖先,就看找共同祖先的时候,得到的值,是否是其中一个。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
struct node{
	int data;
	struct node *l,*r;
}; 
const int N=1e4+10; 
int pre[N],mid[N];
struct node *creat(int len,int pre[],int mid[]){ //前序、中序还原二叉树 
	if(len<=0) return NULL;
	struct node *root;
	root=(struct node*)malloc(sizeof(struct node));
	root->data=pre[0];
	int i;
	for(i=0;i<len;i++){
		if(mid[i]==pre[0]) break;
	}
	root->l=creat(i,pre+1,mid);
	root->r=creat(len-i-1,pre+i+1,mid+i+1);
	return root;
}
struct node*find(struct node *root,int x,int y){ //递归找祖先 
	if(root==NULL) return NULL;
	if(x<=root->data&&y>=root->data||x>=root->data&&y<=root->data){ //祖先满足在x和y的之间的条件 
		return root;
	} 
	else if(x<=root->data&&y<=root->data){ //当前结点太大了,向左儿子递归,找祖先,找上面祖先的条件 
		return find(root->l,x,y);
	}
	else if(x>=root->data&&y>=root->data){ //当前节点太小了,向右儿子递归,找祖先 
		return find(root->r,x,y);
	}
	else return NULL;
}
int main(){
	int n,m;
	scanf("%d%d",&m,&n);
	map<int,int> M; //记录结点的 
	for(int i=0;i<n;i++){
		scanf("%d",&pre[i]);
		M[pre[i]]=1;
		mid[i]=pre[i];
	}
	sort(mid,mid+n); //从小到大排序,还原出中序遍历 
	struct node *root;
	root=creat(n,pre,mid); 
	int x,y;
	while(m--){
		scanf("%d%d",&x,&y);
		if(!M[x] && !M[y]){
			printf("ERROR: %d and %d are not found.\n", x, y);
		}
		else if(!M[x]){
			printf("ERROR: %d is not found.\n", x);
		}
		else if(!M[y]){
			printf("ERROR: %d is not found.\n", y);
		}
		else{
			struct node *p=find(root,x,y);
			if(p->data==x){
				printf("%d is an ancestor of %d.\n", x, y);
			}
			else if(p->data==y){
				printf("%d is an ancestor of %d.\n", y, x);
			}
			else {
				printf("LCA of %d and %d is %d.\n", x, y, p->data);
			}
		}
	}
	return 0;
}

To be continued
如果你有任何建议或者批评和补充,请留言指出,不胜感激

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值