ZOJ - 3965 Binary Tree Restoring (dfs)

题目

题意

给出一颗二叉树的两种 d f s dfs dfs 序列,即同一层上没有明确的左右子树概念,两种序列可以以根节点-左/右子树-另一颗子树的顺序实现。求一颗满足两种 d f s dfs dfs 序列的二叉树,输出每一个节点的父亲节点。题目保证有解

思路

因为是一颗树的重构,我们可以同时从两棵树的 d f s dfs dfs 序列入手,我们可以分别在两个序列上,找到当前子树所在的区间,并且可以通过判断当前位在两个序列中的值是否相同,来判断当前父亲节点需要两棵子树还是一棵子树。
原因是这样的,因为两个 d f s dfs dfs 序列,是以这样的形势展现的:
r o o t − l s root - ls rootls _ r o o t − l s − r s root - ls - rs rootlsrs _ r o o t − r s root - rs rootrs
r o o t − r s root - rs rootrs _ r o o t − r s − l s root - rs - ls rootrsls _ r o o t − l s root - ls rootls

r o o t − l s root - ls rootls _ r o o t − l s − r s root - ls - rs rootlsrs _ r o o t − r s root - rs rootrs
r o o t − l s root - ls rootls _ r o o t − l s − r s root - ls - rs rootlsrs _ r o o t − r s root - rs rootrs
那么当我们确定一个根后,如果他们的后一个节点是相同的,我们可以判断后一个节点引导了一个连在当前根的子树。如果当前根的后一个值在两个序列中是不同的,那么这两个值各自引导了一个连在当前根的子树。儿通过查找两个值的两个序列中的位置,我们可以确定子树所在的序列,然后递归调用。
需要注意的一点是,因为我们的树是二叉的,所以当前根有两棵子树时,是不用考虑的,因为子树长度是确定的,而如果当前根只有一棵子树的话,我们需要判断是否存在另一颗子树。

代码

递归调用实现

const int maxn = 1e5+5;
int a[maxn], b[maxn], pa[maxn], pb[maxn];
int pre[maxn];

void build(int root, int la, int lb, int &len){
    if(len == 0) return;

    if(a[la] != b[lb]){
        pre[a[la]] = root;
        pre[b[lb]] = root;  // la-ls, lb-rs;

        int ls_la = la+1, ls_ra = pa[b[lb]]-1;
        int ls_len = ls_ra - ls_la + 1;
        int rs_lb = lb+1, rs_rb = pb[a[la]]-1;
        int rs_len = rs_rb - rs_lb + 1;
        int ls_lb = pb[a[la]]+1;
        int rs_la = pa[b[lb]]+1;

        build(a[la], ls_la, ls_lb, ls_len); // ls
        build(b[lb], rs_la, rs_lb, rs_len); // rs
        
        len = ls_len + rs_len + 2;
        return ;
    }
    else {
        pre[a[la]] = root;
        int ls_la = la+1, ls_lb = lb+1, ls_len = len-1;
        build(a[la], ls_la, ls_lb, ls_len);
        if(ls_len+1 < len){
            int rs_root = a[la+ls_len+1], rs_la = la+ls_len+2, rs_lb = lb+ls_len+2;
            int rs_len = len - ls_len - 2;
            pre[rs_root] = root;

            build(rs_root, rs_la, rs_lb, rs_len);
            len = ls_len + rs_len + 2;
        }
    }
}


int main()
{
    int T, n;
    sd(T);
    while(T--){
        sd(n);
        rep(i, 0, n) {
            sd(a[i]);
            pa[a[i]] = i;
        }
        rep(i, 0, n) {
            sd(b[i]);
            pb[b[i]] = i;
        }

        build(0, 0, 0, n);

        rep(i, 1, n+1){
            printf("%d%c", pre[i], i==n?'\n':' ');
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值