根据树的先序遍历和后序遍历得到中序遍历,如果树是唯一输出Yes,否则输出No。然后输出中序遍历(如果不唯一,随便输出那一棵)。
题目读起来,写起来不轻松。我主要将树分为三种情况:
1、有一棵子树,则树是不唯一,默认为左子树。
2、有左右子树
3、没有子树,为叶子节点。
这一题难点跟以前类似的题目一样,要把边界写对。我是根据先序在后序的上位置进行匹配,然后计算中序的下标,一个个对应赋值,还要计算中序遍历的下标,难度又增加,短时间内是很难写出来。
成功通过后,看了一下别人的思路,发现写的是又简洁又清晰,是根据先序和后序,对树进行遍历,直接用队列存储值,这样就省去了求中序下标的麻烦。根据别人的思路,我对自己的代码进行了小优化。
事后想想,自己之所以这么写,也是自己对树的遍历理解不够,在写的过程是边写代码边写注释理思路,树的先序遍历和后序遍历的一些特性没有掌握。以前知道先序和后序不能得到唯一棵树,但不知道为什么也没有去深究,今天这一题算是让我对这明白了一些。
(用时:3:29:30.59)
#include <bits/stdc++.h>
using namespace std;
bool flag = true;
int t=0;
//根据别人的思路,对自己的代码进行优化
void GetMidOrder1(int preOrder[],int postOrder[],int preIndex,int preRight,int postLeft,int postRight,int midOrder[])
{
if(postLeft>=postRight) {
return;
}
//先序遍历中左节点位置
int preleftIndex = preIndex +1;
//当前节点后序遍历中的位置
int postIndex = postRight - 1;
//后序遍历中左节点的位置
int postleftIndex = -1;
//有子节点
if(preleftIndex<preRight ) {
for(int i=postLeft; i<postRight; i++) {
if(preOrder[preleftIndex] == postOrder[i]) {
postleftIndex = i;
break;
}
}
//右子树的节点数
int rightNum = 0;
//只有一个子节点 当做是左子树
if(postleftIndex==postIndex-1 ){
//只有一棵子树,不能确定是左还是右
flag = false;
GetMidOrder1(preOrder,postOrder,preleftIndex,preRight,postLeft,postIndex,midOrder);
midOrder[t++] = preOrder[preIndex];
}
//有左右子树
else{
//右子树的节点个数
rightNum = postIndex-postleftIndex -1;
//后序遍历的根的右节点在根节点的左边一位
int postRightIndex = postIndex - 1;
//先序遍历上的右节点的位置
int preRightIndex = -1;
for(int i=preIndex +2; i<preRight; i++) {
if(postOrder[postRightIndex] == preOrder[i]) {
preRightIndex = i;
break;
}
}
//左子树 后序遍历范围是左边界 到 根节点减去右子树的节点数
GetMidOrder1(preOrder,postOrder,preleftIndex,preRightIndex, postLeft,postIndex -rightNum ,midOrder);
midOrder[t++] = preOrder[preIndex];
//右子树的后序遍历范围是根节点减去右子树的节点数 到 根节点
//isleft+1 右子树在中序遍历中往右移
GetMidOrder1(preOrder,postOrder,preRightIndex,preRight,postIndex-rightNum,postIndex,midOrder);
}
}
else{
midOrder[t++] = preOrder[preIndex];
}
}
void GetMidOrder(int preOrder[],int postOrder[],int preIndex,int preRight,int postLeft,int postRight,int midOrder[],int isleft)
{
if(postLeft>=postRight) {
return;
}
//先序遍历中左节点位置
int preleftIndex = preIndex +1;
//当前节点后序遍历中的位置
int postIndex = postRight - 1;
//后序遍历中左节点的位置
int postleftIndex = -1;
//有子节点
if(preleftIndex<preRight ) {
for(int i=postLeft; i<postRight; i++) {
if(preOrder[preleftIndex] == postOrder[i]) {
postleftIndex = i;
break;
}
}
int rightNum = 0;
//只有一个子节点 当做是左子树
if(postleftIndex==postIndex-1 ){
//只有一棵子树,不能确定是左还是右
flag = false;
GetMidOrder(preOrder,postOrder,preleftIndex,preRight,postLeft,postIndex,midOrder,isleft);
}
//有左右子树
else{
//右子树的节点个数
rightNum = postIndex-postleftIndex -1;
//后序遍历的根的右节点在根节点的左边一位
int postRightIndex = postIndex - 1;
//先序遍历上的右节点的位置
int preRightIndex = -1;
for(int i=preIndex +2; i<preRight; i++) {
if(postOrder[postRightIndex] == preOrder[i]) {
preRightIndex = i;
break;
}
}
//左子树 后序遍历范围是左边界 到 根节点减去右子树的节点数
GetMidOrder(preOrder,postOrder,preleftIndex,preRightIndex, postLeft,postIndex -rightNum ,midOrder,isleft);
//右子树的后序遍历范围是根节点减去右子树的节点数 到 根节点
//isleft+1 右子树在中序遍历中往右移
GetMidOrder(preOrder,postOrder,preRightIndex,preRight,postIndex-rightNum,postIndex,midOrder,isleft+1 );
}
//后序遍历的左子树与中序遍历的在范围大小上一致,根的右边的节点在中序遍历的范围要向右移一个
midOrder[postleftIndex + 1 + isleft] = preOrder[preIndex];
}
else{
midOrder[postLeft + isleft] = preOrder[preIndex];
}
}
int main()
{
int n;
scanf("%d",&n);
int preOrder[n];
int postOrder[n];
for(int i=0; i<n; i++) {
scanf("%d",&preOrder[i]);
}
for(int i=0; i<n; i++) {
scanf("%d",&postOrder[i]);
}
int midOrder[n];
GetMidOrder1(preOrder,postOrder,0,n,0,n,midOrder);
if(flag){
printf("Yes\n");
}
else{
printf("No\n");
}
for(int i=0; i<n; i++) {
if(i==0) {
printf("%d",midOrder[i]);
} else {
printf(" %d",midOrder[i]);
}
}
printf("\n");
return 0;
}