题意:给你一棵树的前序和后序遍历,问你它的中序遍历是否唯一,并且给出任意一种中序遍历。
思路:判断中序遍历是否唯一,我们可以通过每个非叶子节点是否都是有两个孩子,如果只有一个孩子,那他的子树既可以是左子树又可以是又子树,所以就不唯一了。
那怎么判断是否有只有一个孩子的结点。
可以看看这个博客:点击打开链接
已知前序遍历和后序遍历序列,是无法确定一棵二叉树的,原因在于如果只有一棵子树可能是左孩子也有可能是右孩子。由于只要输出其中一个方案,所以假定为左孩子即可。下面就是如何根据前序和后序划分出根节点和左右孩子,这里需要定义前序和后序的区间范围,分别为[preL,preR],[postL,postR]。
一开始区间都为[1,n],可以发现前序的第一个和后序的最后一个为根节点root,前序的第二个值val为其某子树的根节点(但还无法确定是左孩子or右孩子)。在后序中找对应的值所在的位置postIdx,则postIdx之前的节点均为val的孩子节点,统计其个数num。那么我们就可以划分区间:
若num个数=preR-preL-1,即val后面的个数都是其子节点,那么二叉树不唯一,将其作为root的左子树处理。
否则划分为左子树区间和右子树对应的前序和后序区间,顺便更新下root的左孩子preL+1,右孩子preL+num+2:
preOrder:[preL+1,preL+num+1],postOrder:[postL,postIdx];
preOrder:[preL+num+2,preR],postOrder:[postIdx+1,postR-1];
然后递归划分即可
拿样例举例:
1 (2) [3 {4 6 7} <5>]
(2) [{6 7 4} <5> 3] 1
不同的括号对应不同的子树区间
第一次递归划分了(2)-(2),[3 4 6 7 5]-[6 7 4 5 3]
由于(2)只有一棵,不继续划分。
第二次递归划分了{4 6 7}-{6 7 4},<5>-<5>
第三次递归划分了(6)-(6),(7)-(7)
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 35;
int n, cnt, pre[maxn], in[maxn], post[maxn];
bool isUnique;
void build(int preL, int preR, int postL, int postR)
{
if(preL == preR) { in[cnt++] = pre[preL]; return ; }
if(preL > preR) return ;
int idx;
for(int i = postL; i < postR; i++)
{
if(pre[preL+1] == post[i])
{
idx = i;
break;
}
}
int numL = idx-postL;
if(preR-preL-1 == numL) isUnique = 0;
build(preL+1, preL+1+numL, postL, idx);
in[cnt++] = pre[preL];
// cout << pre[preL] << endl;
build(preL+numL+2, preR, idx+1, postR-1);
}
int main(void)
{
while(cin >> n)
{
cnt = 1;
isUnique = 1;
for(int i = 1; i <= n; i++) scanf("%d", &pre[i]);
for(int i = 1; i <= n; i++) scanf("%d", &post[i]);
build(1, n, 1, n);
puts(isUnique ? "Yes" : "No");
for(int i = 1; i <= n; i++) printf("%d%c", in[i], i==n ? '\n' : ' ');
}
return 0;
}