1151 LCA in a Binary Tree(两结点的最近公共祖先)

 好难啊!!!自己做是一个错误,加两个超时,呜呜呜。。。

大致题意就是给出一棵树的先序、中序遍历序列,可以构造一棵树。然后给两个顶点,找出这两个结点的最近公共祖先。

思路分析:

1,什么是最近公共祖先?就是说给定 树中两个结点u,v,一定能找到一个且只能找到一个结点root,使得

情况一,root的左子树是u(或者v),右子树是v(或者u)。

情况二,root本身是u(或者v),root的左子树或者右子树包含v(或者u)。

情况三,root既是u也是v。(测试点2)

2,因为题中给出了树的中序、先序遍历序列,所以本可以 以此构造一棵树,但实际上不用建树(如果充分理解建树过程),因为在建树过程中是可以实现(先序、中序、后序)遍历操作的,所以我们可以借助 (先序、中序、后序)遍历获得每层的根结点和上面的思路分析 1 来查找最近公共祖先。

 

 最开始代码,其中遍历左、右子树操作,以及查找结点是否在二叉树中  这三个顺序遍历操作,导致测试点4,5超时;一个u==v的情况未考虑导致测试点2答案错误。

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 const int maxn = 10010;
 5 int m,n,pre[maxn],in[maxn],u,v,flag1,flag2;
 6 
 7 //先序+中序 = 二叉树
 8 void create(int preL,int preR,int inL,int inR) {//可以在建树过程中(实际并没有建树),根据后序遍历来查找最近公共祖先
 9     if(inL > inR) return ;
10     int k,leftNum;
11     for(k = inL; k < inR; ++k)
12         if(in[k] == pre[preL]) break;
13     leftNum = k-inL;
14     create(preL+1,preL+leftNum,inL,k-1);
15     create(preL+leftNum+1,preR,k+1,inR);
16     int L = -1 ,R = -1;
17     for(int i = inL; i <= k-1; ++i) //遍历左子树,两个遍历操作导致超时
18         if(in[i] == u || in[i] == v) L = i;
19     for(int i = k+1; i <= inR; ++i)//遍历右子树
20         if(in[i] == u || in[i] == v) R = i;
21     if(L != -1 && R != -1)//情况一 
22         printf("LCA of %d and %d is %d.\n",u,v,in[k]);
23     if((L != -1||R != -1)&&(in[k] == u||in[k] == v)) //情况二 
24         printf("%d is an ancestor of %d.\n",in[k],in[L!=-1?L:R]);
25 }
26 
27 int main() {
28     cin>>m>>n;
29     for(int i = 0; i < n; ++i) scanf("%d",&in[i]);
30     for(int i = 0; i < n; ++i) scanf("%d",&pre[i]);
31     for(int i = 0; i < m; ++i) {
32         scanf("%d%d",&u,&v);
33         flag1 = flag2 = 1;
34         for(int j = 0; j < n && (flag1 || flag2); ++j) { //顺序遍历,超找结点是否在树中
35             if(u == in[j]) flag1 = 0;
36             if(v == in[j]) flag2 = 0;
37         }
38         if(flag1 && flag2) printf("ERROR: %d and %d are not found.\n",u,v);
39         else if(flag1) printf("ERROR: %d is not found.\n",u);
40         else if(flag2) printf("ERROR: %d is not found.\n",v);
41         else {
42             if(u == v)     printf("%d is an ancestor of %d.\n",u,v);//,必须特判。不然测试点2过不去
43             else create(0,n-1,0,n-1);
44         }
45     }
46     return 0;
47 }

改用map记录中序遍历序列各个结点的位置下标,解决顺序遍历导致超时的问题。

 1 #include<iostream>
 2 #include<unordered_map>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn = 10010;
 6 int m,n,pre[maxn],in[maxn],u,v,flag;
 7 unordered_map<int,int> pos; //记录结点在中序遍历序列的下标
 8 
 9 void LCA(int preL,int preR,int inL,int inR) {
10     if(inL > inR) return ;
11     int k = pos[pre[preL]],inU = pos[u],inV = pos[v];
12     int leftNum = k-inL;
13     LCA(preL+1,preL+leftNum,inL,k-1);
14     LCA(preL+leftNum+1,preR,k+1,inR);
15     if(((inL <= inU&&inU < k)&&(k < inV&&inV<=inR))||(inL <= inV&&inV < k)&&(k < inU&&inU<=inR)) //情况一
16         printf("LCA of %d and %d is %d.\n",u,v,in[k]);
17     else if(inU == k&&((inL<=inV&&inV < k)||(k < inV&&inV<=inR)))//情况二
18         printf("%d is an ancestor of %d.\n",u,v);
19     else if(inV == k&&((inL<=inU&&inU < k)||(k < inU&&inU<=inR)))//情况二
20         printf("%d is an ancestor of %d.\n",v,u);
21 }
22 
23 int main() {
24     cin>>m>>n;
25     for(int i = 1; i <= n; ++i) scanf("%d",&in[i]),pos[in[i]] = i;
26     for(int i = 1; i <= n; ++i) scanf("%d",&pre[i]);
27     for(int i = 0; i < m; ++i) {
28         scanf("%d%d",&u,&v);
29         if(pos[u] == 0 && pos[v] == 0) printf("ERROR: %d and %d are not found.\n",u,v);
30         else if(pos[u] == 0||pos[v] == 0) printf("ERROR: %d is not found.\n",pos[u] == 0?u:v);
31         else {
32             if(u == v)     printf("%d is an ancestor of %d.\n",u,v);//,必须特判。不然测试点2过不去
33             else LCA(1,n,1,n);
34         }
35     }
36     return 0;
37 }

 参考刘婼的代码,思路如下:

不用建树~已知某个树的根结点,若a和b在根结点的左边,则a和b的近公共祖先在当前⼦子树 根结点的左⼦子树寻找,如果a和b在当前⼦子树根结点的两边,在当前⼦子树的根结点就是a和b的近公共 祖先,如果a和b在当前⼦子树根结点的右边,则a和b的近公共祖先就在当前⼦子树的右⼦子树寻找。中序 加先序可以唯⼀一确定⼀一棵树,在不不构建树的情况下,在每⼀一层的递归中,可以得到树的根结点,在此 时并⼊入lca算法可以确定两个结点的公共祖先~

 1 #include<iostream>
 2 #include<unordered_map>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn = 10010;
 6 int m,n,pre[maxn],in[maxn],u,v,flag;
 7 unordered_map<int,int> pos;
 8 
 9 void LCA(int inL,int inR,int preRoot,int u,int v) {
10     if(inL > inR) return ;
11     int inRoot = pos[pre[preRoot]],inU = pos[u],inV = pos[v];
12     if((inU < inRoot && inRoot < inV)||(inU > inRoot && inRoot > inV)) //情况一
13         printf("LCA of %d and %d is %d.\n",u,v,in[inRoot]);
14     else if(inU == inRoot)//情况二
15         printf("%d is an ancestor of %d.\n",u,v);
16     else if(inV == inRoot)//情况二
17         printf("%d is an ancestor of %d.\n",v,u);
18     else if(inU < inRoot && inV < inRoot) //遍历左子树 
19         LCA(inL,inRoot-1,preRoot+1,u,v);
20     else if(inU > inRoot && inV > inRoot) //遍历右子树 
21         LCA(inRoot+1,inR,preRoot+1+(inRoot-inL),u,v);
22 }
23 
24 int main() {
25     cin>>m>>n;
26     for(int i = 1; i <= n; ++i) scanf("%d",&in[i]),pos[in[i]] = i;
27     for(int i = 1; i <= n; ++i) scanf("%d",&pre[i]);
28     for(int i = 0; i < m; ++i) {
29         scanf("%d%d",&u,&v);
30         if(pos[u] == 0 && pos[v] == 0) printf("ERROR: %d and %d are not found.\n",u,v);
31         else if(pos[u] == 0||pos[v] == 0) printf("ERROR: %d is not found.\n",pos[u] == 0?u:v);
32         else LCA(1,n,1,u,v);
33     }
34     return 0;
35 }

posted @ 2020-03-13 20:56  tangq123  阅读(171)  评论(0编辑  收藏  举报