1151 LCA in a Binary Tree (30 分)

44 篇文章 0 订阅

 题目大意:已知一个树的前序和中序序列,求两个结点的最近公共祖先

本着试一试的做法,直接建树,然后套倍增求LCA的算法,还好数据不是很大,没卡爆数组。

法1:

#include <iostream>
#include <unordered_set>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 10010;
int n,m,pre[N],iner[N],dis[N*100],fa[N*100][16];
struct node
{
    int v;
    node*left,*right;
    node(int v) : v(v),left(NULL),right(NULL){}
}*tr;
unordered_set<int>se;
node *build(int preL,int preR,int inerL,int inerR)//前中建树
{
    if(preL > preR) return NULL;
    int k = inerL;
    node* t = new node(pre[preL]);
    se.insert(pre[preL]);
    while(pre[preL] != iner[k]) k ++;//前序第一个结点就是根,在中序里面找根
    t -> left = build(preL + 1,preL + k - inerL,inerL,k - 1);
    t -> right = build(preL + k - inerL + 1,preR,k + 1,inerR);
    return t;
}
void dfs(node *t,int d)
{   
    if(t->left != NULL)
    {   
        int a = t ->left ->v;
        if(dis[a] > d + 1)
        {
        dis[a] = d + 1;fa[a][0] = t->v;
        for(int k = 1; k <= 15; k ++) fa[a][k] = fa[fa[a][k - 1]][k - 1];
        dfs(t->left,d + 1);
        }
    }
    if(t->right != NULL)
    {   
        int a = t ->right ->v;
        if(dis[a] > d + 1)
        {
        dis[a] = d + 1;fa[a][0] = t->v;
        for(int k = 1; k <= 15; k ++) fa[a][k] = fa[fa[a][k - 1]][k - 1];
        dfs(t->right,d + 1);
        }
    }
}
int lca(int a,int b)
{
    if(dis[a] < dis[b]) swap(a,b);
    for(int k = 15; k >= 0; k --)
        if(dis[fa[a][k]] >= dis[b]) a = fa[a][k];
    if(a == b) return a;
    for(int k = 15; k >= 0; k --)
        if(fa[a][k] != fa[b][k]) a = fa[a][k],b = fa[b][k];
    return fa[a][0];
}
int main()
{    
    scanf("%d%d",&n,&m);
    for(int i = 0; i < m; i ++) scanf("%d",&iner[i]);
    for(int i = 0; i < m; i ++) scanf("%d",&pre[i]);
    tr = build(0,m - 1,0,m - 1);
    memset(dis,0x3f,sizeof dis);
    dis[0] = 0;dis[pre[0]] = 1;
    dfs(tr,1);
    for(int i = 0; i < n; i ++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if(se.count(a) && !se.count(b)) printf("ERROR: %d is not found.\n",b);
        else if(!se.count(a) && se.count(b)) printf("ERROR: %d is not found.\n",a);
        else if(!se.count(a) && !se.count(b)) printf("ERROR: %d and %d are not found.\n",a,b);
        else
        {
            int t = lca(a,b);
            if(t == a) printf("%d is an ancestor of %d.\n",a,b);
            else if(t == b) printf("%d is an ancestor of %d.\n",b,a);
            else printf("LCA of %d and %d is %d.\n",a,b,t);   
        }
    }
    return 0;

}

法2:也是稳妥做法,防止权值过大,或出现负权值

#include <iostream>
#include <unordered_map>
#include <cstring>
using namespace std;
const int N = 10010;
int n,m,pre[N],iner[N];
struct node
{
    int v;
    node*left,*right,*parent;
    node(int v) : v(v),left(NULL),right(NULL),parent(NULL){}
}*tr;
unordered_map<int,node*>mp;
node *build(int preL,int preR,int inerL,int inerR)//前中建树
{
    if(preL > preR) return NULL;
    int k = inerL;
    node* t = new node(pre[preL]);
    while(pre[preL] != iner[k]) k ++;//前序第一个结点就是根,在中序里面找根
    t -> left = build(preL + 1,preL + k - inerL,inerL,k - 1);
    t -> right = build(preL + k - inerL + 1,preR,k + 1,inerR);
    if(t -> left != NULL) t -> left -> parent = t;
    if(t -> right != NULL) t -> right -> parent = t;
    mp[pre[preL]] = t;
    return t;
}
int depth(node *a)
{
    int res = 0;
    while(a -> parent != NULL) a = a -> parent,res ++;
    return res;
}
int lca(node *a,node *b)
{
    int d1 = depth(a),d2 = depth(b);
    while(d1 > d2) a = a -> parent,d1 --;//走到同一深度
    while(d1 < d2) b = b -> parent,d2 --;
    if(a == b) return a -> v;
    while(a != b) a = a -> parent,b = b -> parent,d1 --,d2 --;//一起向上走
    return a -> v;
}
int main()
{    
    scanf("%d%d",&n,&m);
    for(int i = 0; i < m; i ++) scanf("%d",&iner[i]);
    for(int i = 0; i < m; i ++) scanf("%d",&pre[i]);
    tr = build(0,m - 1,0,m - 1);
    for(int i = 0; i < n; i ++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if(mp.count(a) && !mp.count(b)) printf("ERROR: %d is not found.\n",b);
        else if(!mp.count(a) && mp.count(b)) printf("ERROR: %d is not found.\n",a);
        else if(!mp.count(a) && !mp.count(b)) printf("ERROR: %d and %d are not found.\n",a,b);
        else
        {
            int t = lca(mp[a],mp[b]);
            if(t == a) printf("%d is an ancestor of %d.\n",a,b);
            else if(t == b) printf("%d is an ancestor of %d.\n",b,a);
            else printf("LCA of %d and %d is %d.\n",a,b,t);   
        }
    }
    return 0;

}

参考了柳神的代码,发现不需要建树,直接递归求解代码还非常精简。

#include <iostream>
#include <unordered_map>
using namespace std;
const int N = 10010;
int n,m,pre[N],iner[N];
unordered_map<int,int>mp;
void lca(int inL,int inR,int u,int a,int b)
{
    if(inL > inR) return ;
    int l = mp[a],r = mp[b],root = mp[pre[u]];
    if(l < root && r < root) lca(inL,root - 1,u + 1,a,b); //a和b都在当前子树根的左边
    else if(l > root && r > root) lca(root + 1,inR,u + root - inL + 1,a,b);//a和b都在当前子树根的右边
    else if (l < root && r > root || l > root && r < root)//分布在左右的话,root就是最近公共祖先
        printf("LCA of %d and %d is %d.\n",a,b,iner[root]);   
    else if(l == root)
        printf("%d is an ancestor of %d.\n",a,b);
    else printf("%d is an ancestor of %d.\n",b,a);
}
int main()
{    
    scanf("%d%d",&n,&m);
    for(int i = 0; i < m; i ++) 
    {
        scanf("%d",&iner[i]);
        mp[iner[i]] = i;//存位置
    }
    for(int i = 0; i < m; i ++) scanf("%d",&pre[i]);
    for(int i = 0; i < n; i ++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if(mp.count(a) && !mp.count(b)) printf("ERROR: %d is not found.\n",b);
        else if(!mp.count(a) && mp.count(b)) printf("ERROR: %d is not found.\n",a);
        else if(!mp.count(a) && !mp.count(b)) printf("ERROR: %d and %d are not found.\n",a,b);
        else lca(0,m - 1,0,a,b);
    }
    return 0;

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值