题目大意:
给定一颗含有N个节点的前序和中序序列,要求给定任意2个节点,需要输出其最近公共祖先。
算法思路:
这里和1143一样给出2种思路,一种不用建树,一种需要建树。
算法思路1(不用建树):
我们借鉴建树的递归过程完成树的部分搜索,如果当前搜索的子树的根节点和查询节点U和V相等,说明其中之一就是祖先,直接保存并返回即可,否则获取U和V是否在左子树或者右子树的标志,如果都在左子树,那么就都往左子树搜索,如果都在右子树,那么就都往右子树搜索,否则说明当前子树的根节点就是U和V的最近公共祖先,直接保存并返回即可,具体做法就是,在输入中序序列的时候,使用pos保存每一个节点在中序遍历序列中的位置,那么每一次递归查找中序序列根节点的位置就可以替换为int k = pos[pre[preL]];
然后再使用uInRight和vInRight分别表示U和V是否在右子树,true表示在右子树,获取的方法也很简单,就是pos[U]>k
和pos[V]>k
即可,然后再根据uInRight和vInRight的值,要么递归搜索,要么保存祖先ancestor = pre[preL];
并返回即可。
算法思路2(建树):
首先根据前序和中序建立一颗二叉树,然后利用层序遍历的方法获得每一个节点的父亲和其所在层数,对于输入的节点x和y,如果节点所在层数一样,那么只要两者不相等就一同向上搜索,直到相遇,其相遇节点即为最近公共祖先,否则让层数更大的那个先向上走,直到和另外一个节点层数相同,然后再同时向上,直到相遇为止。
注意点:
- 1、对于算法思路一出现的测试点4超时现象,需要使用pos记录每一个节点在中序序列的位置,这是关键。
提交结果:
AC代码1(推荐):
#include<cstdio>
using namespace std;
const int maxn = 0x3ffffff;
int N,M;// 节点数目和测试数目
int pre[maxn],in[maxn];
bool isValid[maxn];
int pos[maxn];// 每一个节点在中序序列中的下标,不会超时的关键
void createTree(int preL,int preR,int inL,int inR,int &ancestor,int U,int V){
if(preL>preR) return;
if(pre[preL]==U||pre[preL]==V){
ancestor = pre[preL]==U?U:V;
return ;
}
// 找到根节点在中序序列中的位置
int k = pos[pre[preL]];// 直接获取位置
int numOfLeft = k-inL;// 左子树节点个数
bool uInRight = pos[U]>k,vInRight = pos[V]>k;// U和V在左子树还是右子树,true在右子树
if(uInRight&&vInRight){
// U和V都在右子树
createTree(preL+numOfLeft+1,preR,k+1,inR,ancestor,U,V);
} else if(!uInRight&&!vInRight){
// U和V都在左子树
createTree(preL+1,preL+numOfLeft,inL,k-1,ancestor,U,V);
} else {
// U和V分别在左右子树
ancestor = pre[preL];
return;
}
}
int main(){
scanf("%d %d",&M,&N);
for (int i = 0; i < N; ++i) {
scanf("%d",&in[i]);
isValid[in[i]] = true;
pos[in[i]] = i;
}
for (int i = 0; i < N; ++i) {
scanf("%d",&pre[i]);
}
for (int j = 0; j < M; ++j) {
int u,v;
scanf("%d %d",&u,&v);
if(!isValid[u]&&!isValid[v]){
printf("ERROR: %d and %d are not found.\n",u,v);
} else if(!isValid[u]){
printf("ERROR: %d is not found.\n",u);
} else if(!isValid[v]){
printf("ERROR: %d is not found.\n",v);
} else {
int ancestor = -1;
createTree(0,N-1,0,N-1,ancestor,u,v);
if (ancestor==u||ancestor==v){
printf("%d is an ancestor of %d.\n",ancestor,ancestor==u?v:u);
} else {
printf("LCA of %d and %d is %d.\n",u,v,ancestor);
}
}
}
return 0;
}
AC代码2:
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
struct node{
int data;
int level;
node* lchild;
node* rchild;
node* parent;
};
const int maxn = 200005;
int pre[maxn],in[maxn];
int Hash[maxn];//PAT不能用hash
node* preN[maxn];
int num = 0;//先序遍历下标
node* newNode(int x){
node* w = new node;
w->data =x;
w->level = 1;
w->lchild=w->rchild=w->parent=NULL;
return w;
}
//根据当前子树的前序排序序列和中序排序序列构建二叉树
node* create(int preL,int preR,int inL,int inR){
if(preL>preR){//当前子树为空,没有结点
return NULL;
}
node* root = newNode(pre[preL]);
//首先找到中序遍历序列中等于当前根结点的值得下标
int i;
for(i=inL;i<=inR;++i){
if(in[i]==pre[preL]){
break;
}
}
int numLeft = i-inL;
//往左子树插入,左子树先序遍历区间为[preL+1,preL+numleft],中序遍历区间为[inL,i-1]
root->lchild = create(preL+1,preL+numLeft,inL,i-1);
//往右子树插入,右子树先序遍历区间[preL+numLeft+1,preR],中序遍历区间为[i+1,inR]
root->rchild = create(preL+numLeft+1,preR,i+1,inR);
return root;
}
//先序遍历
void tre(node* root){
if(root==NULL) return;
preN[num++] = root;
tre(root->lchild);
tre(root->rchild);
}
//层序遍历
void layerOrder(node* root){
queue<node*> q;
q.push(root);
while(!q.empty()){
node* w = q.front();
q.pop();
if(w->lchild!=NULL){
w->lchild->level = w->level+1;
w->lchild->parent = w;
q.push(w->lchild);
}
if(w->rchild!=NULL){
w->rchild->level = w->level+1;
w->rchild->parent = w;
q.push(w->rchild);
}
}
}
int main(){
int m,n;//测试的结点对数和结点数目
scanf("%d %d",&m,&n);
memset(Hash,0,sizeof(Hash));
for(int i=0;i<n;++i){
scanf("%d",&in[i]);
}
for(int i=0;i<n;++i){
scanf("%d",&pre[i]);
Hash[pre[i]] = i+1;
}
node* root = create(0,n-1,0,n-1);
layerOrder(root);
//先序遍历
tre(root);
//测试
int x,y;
for(int i=0;i<m;++i){
scanf("%d %d",&x,&y);
if(Hash[x]!=0&&Hash[y]!=0){
//均是树中结点
node* w1 = preN[Hash[x]-1];//通过结点的值找到前序遍历数组的下标,然后再找到对应结点
node* w2 = preN[Hash[y]-1];
if(w1->level==w2->level){
//2个结点在同一层
if(w1==w2){
printf("%d is an ancestor of %d.\n",x,y);
} else{
//2结点不相等,则同时往上走
node* t1 = w1;
node* t2 = w2;
while(t1->parent!=NULL){
t1 = t1->parent;
t2 = t2->parent;
if(t1==t2){
printf("LCA of %d and %d is %d.\n",x,y,t1->data);
break;
}
}
}
}else{
//2结点不在同一层,让层数较大的先往上走
node* max = w1->level>w2->level?w1:w2;
node* min = w1->level<w2->level?w1:w2;
while(max->level!=min->level&&max->parent!=NULL){
max = max->parent;
}
//然后判断min是否和max相等
if(min==max){
//说明min是max的祖先,但此时max已经更改所以得重新赋值
max = w1->level>w2->level?w1:w2;
printf("%d is an ancestor of %d.\n",min->data,max->data);
} else{
//2结点不相等,则同时往上走
while(max->parent!=NULL){
max = max->parent;
min = min->parent;
if(max==min){
printf("LCA of %d and %d is %d.\n",x,y,min->data);
break;
}
}
}
}
}else if(Hash[x]==0&&Hash[y]!=0){
printf("ERROR: %d is not found.\n",x);
}else if(Hash[x]!=0&&Hash[y]==0){
printf("ERROR: %d is not found.\n",y);
}else{
printf("ERROR: %d and %d are not found.\n",x,y);
}
}
return 0;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。