题目描述
链接
给出一棵树的结点个数n,以及它的前序遍历和后序遍历,输出它的中序遍历,如果中序遍历不唯一就输出No,且输出其中一个中序即可,如果中序遍历唯一就输出Yes,并输出它的中序
分析
- 分析题目所给的正反样例,可以发现,最后递归到单一子树后,左根是它,右根也是它,就不唯一了。此时,可以随便指定为右孩子或者左孩子
- 具体来说,对先序从前往后找,对后序从后往前找,找到左右子树的根,从而确定左右子树的范围进行递归
- 当递归到某处,发现当前结点!!的左子树的根和右子树的根重合了(i==prel+1)话((i)是右子树的根,(prel)是当前根结点,(prel+1)是左子树的根),则向下递归时,只向右子树递归
- 另外还要注意,递归到最后,只剩一个结点即(prel==prer),就直接return root了,不用再向下递归了
- 画下图,模拟下过程就好
#include<bits/stdc++.h>
using namespace std;
const int maxn = 40;
int pre[maxn],post[maxn];
int n;
struct node{
int data;
node *lchild,*rchild;
};
//root为后序序列的根结点,st,ed为先序的范围
vector<int> ans;
bool flag = true;
node *create(int prel, int prer, int postl, int postr){
if(prel > prer) return NULL;
//建立此状态下的根结点
node *root = new node;
root->data = pre[prel];
root->lchild = NULL;
root->rchild = NULL;
if(prel==prer){
return root;
}
//找左右子树的范围
//在先序里面找到右子树根节点的位置
int i;
for(i=prel;i<=prer;i++){
if(post[postr-1] == pre[i]){
break;
}
}
if(i - prel > 1){
//左子树的范围: prel+1,i-1, 长度i-1-prel
root->lchild = create(prel+1,i-1,postl,postl+i-prel-2); //左子树的范围
//右子树的范围:
root->rchild = create(i, prer, postl+i-prel-1, postr-1);
}else{ //i==prel+1 即左右子树的根节点重合了
flag = false;
root->rchild = create(i, prer, postl+i-prel-1, postr-1); //就当成右子树
}
return root;
}
void inorder(node *root){
if(!root) return;
inorder(root->lchild);
ans.push_back(root->data);
inorder(root->rchild);
}
int main(){
cin>>n;
for(int i=0;i<n;i++) cin>>pre[i];
for(int i=0;i<n;i++) cin>>post[i];
node *root = create(0,n-1,0,n-1);
inorder(root);
if(flag) printf("Yes
");
else printf("No
");
for(int i=0;i<ans.size();i++){
if(i==0)printf("%d",ans[i]);
else printf(" %d",ans[i]);
}
printf("
");
}