爆刷PAT(甲级)——之【1119 】Pre- and Post-order Traversals (30 分)——先序后序建立中序遍历

 题意:分别输入一个先序和后序遍历的序列,给出对应的中序遍历,并判断此中序遍历是否唯一。

 

方法与学习过程:本题就是赤裸裸的如何由先序和后序遍历,进行建树or进行中序遍历。学习了三位博客的内容,我就不献丑赘述了~

柳神的代码是用来膜的,看一下柳神的宏观思路:https://blog.csdn.net/liuchuo/article/details/52505179

这位兄弟的博客主要是由图文的一个描述,可以更好的理解什么情况下,中序遍历是不唯一的?但是代码就比较冗长了...https://blog.csdn.net/li1615882553/article/details/88061615

这位道友的博客主要是和柳神一样,代码写的很好噢!37行代码,思路清晰而且很通俗易懂,本题代码我是借鉴了他的。https://blog.csdn.net/rain722/article/details/52596149

 

 思考:学习了【先序后序建树得中序】之后呢,自然而然会将其与【先序中序建树】与【后序中序建树】进行联想。这其中的代码撰写与思考是有一定的异同的,我之前总结了【先序中序建树】与【后序中序建树】的建树模板与思路传送门在这里PAT上也有道题目,是【先序+后序】进行建树的传送门2在这里

首先【先序中序建树】与【后序中序建树】的思考方式,都是先找出根节点在中序中的位置,然后就可以对左右子树进行递归了!故根节点可以在 “先序/中序” 与 “中序” 建立联系,因此切入点是——根节点

而本题目的【先序后序建树得中序】中,没有中序呀!而且根的位置很显然,就是先序的第一个呗。但是我们还是要想办法剥离出左右子树进行递归,所以我们从“某个子树的根”来切入,即”当前根节点的孩子“来切入因为 孩子节点能够在 ”先序“ 和 “后序” 中建立联系。这一点很重要。在下面的代码中,体现这个思想,是在getIn(...)函数的for循环里,找到孩子在后序中的位置

而我们在建立中序遍历的输出序列时候,秉持的依然是“左根右”的方式,所以先对左子树进行遍历,再写入根于in数组中,再对右子树进行遍历。

不论是之前的【先序中序建树】与【后序中序建树】,还是本次的【先序后序建树得中序】建树,核心都在于,根据已有的序列,如何把序列分辨出哪个是根,哪个是左子树的部分,哪个是右子树的部分,就好了,剩下的交给计算机去递归就行了!

 

Code:借鉴了第三位博主的写法,再加上自己的书写习惯,第一次写PAT能有30来行的代码,哭惹 T T

 

#include<cstdio>
#define inf 39
int n,pre[inf],in[inf],post[inf],pos;
bool getIn(int l1,int r1,int l2,int r2)//此时根是pre[l1],而pre[l1+1]是它的孩子
{
    if(l1>r1)return false;
    if(l1==r1)//为什么要特判?否则下面对孩子的访问会越界段错误的
    {
        in[pos++]=pre[l1];//此处是根不是孩子噢
        return true;
    }
    int i;
    for(i=l2; post[i]!=pre[l1+1]; i++); //找出第一个孩子
    int len=i-l2+1;//由于包含此孩子节点,务必要加1
    int tag=1;
    tag&=getIn(l1+1,l1+len,l2,i);
    in[pos++]=pre[l1];//中序遍历的精华,此处是根不是孩子噢
    tag&=getIn(l1+len+1,r1,i+1,r2-1);
    return tag;
}
int main()
{
    scanf("%d",&n);
    for(int i=0; i<n; i++)scanf("%d",&pre[i]);
    for(int i=0; i<n; i++)scanf("%d",&post[i]);
    pos=0;//pos是in统计的下标
    if(getIn(0,n-1,0,n-1))printf("Yes\n");
    else printf("No\n");
    for(int i=0; i<n; i++)printf("%d%c",in[i],i==n-1?'\n':' ');
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值