题意:分别输入一个先序和后序遍历的序列,给出对应的中序遍历,并判断此中序遍历是否唯一。
方法与学习过程:本题就是赤裸裸的如何由先序和后序遍历,进行建树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;
}