题目大意:给出树的节点数,以及前序、后序遍历序列。若树唯一,输出“Yes”,以及中序遍历序列;否则输出“No”,以及任意一个中序遍历序列。
分析:
- 首先回顾 前、中序建树的过程:前序根节点确定,中序不确定,根据前序确定根节点,在中序中找到根,切分左右子树,递归。
- 在看 前、后序建树,前序、后序根节点都确定了,且一个在最前,一个在最后,无法切分左右子树。但必须得想办法,切分。
为什么前序和后序序列无法确定唯一的二叉树?
前序根节点 preL 的后一个 preL+1,一定是根的孩子(不知左右);后序根节点 postR 的前一个 postR-1 ,一定是根的孩子(不知左右)。
- 当 pre[preL+1]!=post[postR−1]pre[preL+1] != post[postR-1]pre[preL+1]!=post[postR−1] 时, => 说明有两个孩子,并且可区分左右,在后序中找到左孩子的根,即可划分出左右子树来。
- pre[preL+1]==post[postR−1]pre[preL+1] == post[postR-1]pre[preL+1]==post[postR−1]时,说明只有一个孩子,但无法确定是左/右孩子,故不唯一!!
由题意可知,当不唯一时,默认将其视为右孩子,建树,中序遍历。
注意点:
- 结束条件。尤其是只有一个元素的区间。
- 输出格式,最后要输出一个换行才算正确。不知为何,总之养成最后输出空行的习惯吧。
代码:
#include<cstdio>
#include<algorithm>
#define MAXN 50
using namespace std;
int n;
int pre[MAXN];
int post[MAXN];
struct node {
int data;
node* lchild;
node* rchild;
};
bool flag = true;
node* createTree(int preL,int preR,int postL,int postR) {
if(preL > preR) return NULL;
// 建立根节点
node* root = new node;
root->data = pre[preL] ;
// 区间长度为 1 时的特判
if(preL == preR) {
root->lchild = root->rchild = NULL;
return root;
}
//判断是否有两个孩子
if(pre[preL+1] != post[postR-1]) {
// 两个孩子
// 在后序序列中找左子树的根节点,切分左右子树
int k = postL;
while(k < postR && post[k] != pre[preL+1]) k++;
int numLeft = k - postL + 1; // 左子树的结点个数
root->lchild = createTree(preL+1,preL+numLeft,postL,k) ;
root->rchild = createTree(preL+numLeft+1,preR,k+1,postR-1);
} else {
// 一个孩子
flag = false;
// 默认当为右孩子来创建
root->lchild = NULL;
root->rchild = createTree(preL+1,preR,postL,postR-1);
}
}
int cnt;
void InOrder(node* root) {
if(root==NULL) return ;
InOrder(root->lchild);
printf("%d",root->data);
++cnt;
if(cnt < n) printf(" ");
else printf("\n");
InOrder(root->rchild);
}
int main() {
// printf("%d",flag);
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]);
}
node* root = createTree(0,n-1,0,n-1);
if(flag)
printf("Yes\n");
else printf("No\n");
InOrder(root);
return 0;
}