题目链接【传送门】
之前写过一次,用的是空间换时间,也就是前序、中序构建二叉树,求解每个结点的从根结点到此结点的路径(逆序)存入map中,对查询的时候,遍历二者的路径找最远的公共元素作为输出,AC是AC了,但是这个代码量……比较累赘
#include
using namespace std;
int M,N,u,v;
vector<int>pre;
vector<int>in;
struct Node{
int val;
Node*left=NULL;
Node*right=NULL;
Node*parent=NULL;
};
Node*build(int inL,int inR,int preL,int preR){
if(inL>inR||preL>preR||inL<0||inR>=N||preL<0||preR>=N){
return NULL;
}
Node* root=new Node;
root->val=pre[preL];
int k;
for(k=inL;k<=inR;k++){
if(in[k]==root->val){
break;
}
}
int num=k-inL;
root->left=build(inL,k-1,preL+1,preL+num);
root->right=build(k+1,inR,preL+num+1,preR);
return root;
}
map<int,vector<int>>path;
map<int,Node*>nodes;
void levelTravel(Node*root){
queue<Node*>q;
q.push(root);
while(!q.empty()){
Node*top=q.front();
nodes[top->val]=top;
q.pop();
if(top->left!=NULL){
top->left->parent=top;
q.push(top->left);
}
if(top->right!=NULL){
top->right->parent=top;
q.push(top->right);
}
}
}
int main(){
scanf("%d%d",&M,&N);
pre.resize(N);
in.resize(N);
for(int i=0;i<N;i++){
scanf("%d",&pre[i]);
}
in=pre;
sort(in.begin(),in.end());
Node*root=build(0,N-1,0,N-1);
levelTravel(root);
for(int i=0;i<M;i++){
scanf("%d%d",&u,&v);
if(find(pre.begin(),pre.end(),u)==pre.end()){
if(find(pre.begin(),pre.end(),v)==pre.end()){
printf("ERROR: %d and %d are not found.\n",u,v);
}else{
printf("ERROR: %d is not found.\n",u);
}
}else{
if(find(pre.begin(),pre.end(),v)==pre.end()){
printf("ERROR: %d is not found.\n",v);
}else{
//寻找两个结点的最近的父辈
int p;
if(path.find(u)==path.end()){
p=u;
while(nodes[p]->parent!=NULL){
path[u].push_back(p);
p=nodes[p]->parent->val;
}
reverse(path[u].begin(),path[u].end());
}
if(path.find(v)==path.end()){
p=v;
while(nodes[p]->parent!=NULL){
path[v].push_back(p);
p=nodes[p]->parent->val;
}
reverse(path[v].begin(),path[v].end());
}
int a=root->val;
for(int i=0;i<min(path[u].size(),path[v].size());i++){
if(path[u][i]==path[v][i]){
a=path[u][i];
}else{
break;
}
}
if(a==u){
printf("%d is an ancestor of %d.\n",a,v);
}else if(a==v){
printf("%d is an ancestor of %d.\n",a,u);
}else{
printf("LCA of %d and %d is %d.\n",u,v,a);
}
}
}
}
return 0;
}
今天重写的时候发现LCA可以不用这么复杂,于是参考【LCA in Binary Trees柳神】的思路用递归的方式重构了一次……结果喜提超时了一个测试点24/30:
#include
using namespace std;
int M,N,u,v;
vector<int>pre,in;
map<int,int>inpos;
map<int,bool>mp;
void LCA(int preL,int preR,int inL,int inR){
int rootPos=preL;
if(u<pre[rootPos]&&v>pre[rootPos]||u>pre[rootPos]&&v<pre[rootPos]){
printf("LCA of %d and %d is %d.\n",u,v,pre[rootPos]);
}else if(u<pre[rootPos]&&u<pre[rootPos]){
//左子树
int k=inpos[pre[rootPos]];
int num=k-inL;
LCA(preL+1,preL+num,inL,k-1);
}else if(u>pre[rootPos]&&u>pre[rootPos]){
//右子树
int k=inpos[pre[rootPos]];
int num=k-inL;
LCA(preL+1+num,preR,k+1,inR);
}else if(u==pre[rootPos]){
printf("%d is an ancestor of %d.\n",u,v);
}else if(v==pre[rootPos]){
printf("%d is an ancestor of %d.\n",v,u);
}
}
int main(){
scanf("%d%d",&M,&N);
pre.resize(N);
for(int i=0;i<N;i++){
scanf("%d",&pre[i]);
mp[pre[i]]=true;
}
in=pre;
sort(in.begin(),in.end());
for(int i=0;i<N;i++){
inpos[in[i]]=i;
}
while(M--){
scanf("%d%d",&u,&v);
if(mp.find(u)==mp.end()){
if(mp.find(v)==mp.end()){
printf("ERROR: %d and %d are not found.\n",u,v);
}else{
printf("ERROR: %d is not found.\n",u);
}
}else{
if(mp.find(v)==mp.end()){
printf("ERROR: %d is not found.\n",v);
}else{
//找到最近公共结点
LCA(0,N-1,0,N-1);
}
}
}
return 0;
}
看了柳神的此题题解,发现……还是自己对BST树了解有待提高。思路是首先用map存储所有key,对输入先判断是不是树中的结点;接着遍历pre数组,由二叉排序树的性质可知,当顺序遍历pre数组时,第一个出现pre[index]<=u&&pre[index]>=v||pre[index]>=u&&pre[index]<=v即为答案结点,按照要求输出即可
AC代码如下(代码量和效率的双赢了属实,但是确实对性质理解不到位很难想到这个解法):
#include
using namespace std;
int N,M,a,b;
vector<int>pre;
map<int,bool>mp;
int main(){
scanf("%d%d",&M,&N);
pre.resize(N);
for(int i=0;i<N;i++){
scanf("%d",&pre[i]);
mp[pre[i]]=true;
}
while(M--){
scanf("%d%d",&a,&b);
if(mp.find(a)==mp.end()){
if(mp.find(b)==mp.end()){
printf("ERROR: %d and %d are not found.\n",a,b);
}else{
printf("ERROR: %d is not found.\n",a);
}
}else{
if(mp.find(b)==mp.end()){
printf("ERROR: %d is not found.\n",b);
}else{
int ans;
for(int i=0;i<N;i++){
if(pre[i]<a&&pre[i]>b||pre[i]>a&&pre[i]<b||pre[i]==a||pre[i]==b){
ans=pre[i];
break;
}
}
if(ans!=a){
if(ans!=b){
printf("LCA of %d and %d is %d.\n",a,b,ans);
}else{
printf("%d is an ancestor of %d.\n",ans,a);
}
}else{
printf("%d is an ancestor of %d.\n",ans,b);
}
}
}
}
return 0;
}
原题如下:
The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U and V as descendants.
A binary search tree (BST) is recursively defined as a binary tree which has the following properties:
Given any two nodes in a BST, you are supposed to find their LCA.
Each input file contains one test case. For each case, the first line gives two positive integers: M (≤ 1,000), the number of pairs of nodes to be tested; and N (≤ 10,000), the number of keys in the BST, respectively. In the second line, N distinct integers are given as the preorder traversal sequence of the BST. Then M lines follow, each contains a pair of integer keys U and V. All the keys are in the range of int.
For each given pair of U and V, print in a line LCA of U and V is A. if the LCA is found and A is the key. But if A is one of U and V, print X is an ancestor of Y. where X is A and Y is the other node. If U or V is not found in the BST, print in a line ERROR: U is not found. or ERROR: V is not found. or 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.