前序后序之所以不能确定一个二叉树的本质就是无法确定左右子树的长度。所以构建的时候枚举左子树的长度。
前序遍历的第一个元素是根节点,后序遍历的最后一个元素是根节点。暴力枚举左孩子的长度,
在每一次枚举的过程中,递归地求这种情况下能否成功构造二叉树。
每一次返回能成功构造二叉树的方案数量,若>1说明在这个前序与后序序列下构造的二叉树不唯一,
且能成功构造了,可以跳出枚举左孩子的循环了。
注意
1.我们枚举的左孩子的长度,是假设这个根节点有左子树,且右子树长度也已知。在这种环境下,
递归地再求左子树的孩子能否构建好,右子树的孩子能否构建好,就能知道这个二叉树能否构建成功。在递归过程中,当不满足前序遍历与后序遍历根节点相同的话,这个树就构建不成功。
2.构建左孩子的方案数lcnt
,构建右孩子的方案数rcnt
总方案数就是lcnt * rcnt
3、递归的同时我们也求中序遍历的结果in
,把左右子树的遍历的根节点记录下来,
in = lin + to_string(pre[l1]) + ' ' + rin
, 当最后为叶子节点的时候,lin
,rin
均为空
就会多出来一个空格,记得删去。lin
:记录左子树中序遍历的结果,rin
:记录右子树中序遍历。lin,rin后面都有空格,所以在根节点pre[l1]
后面只要加一个空格即可。
lin,rin都是到叶子节点结束,所有最后的每次都是到叶子节点的时候返回,所以必定有空格在后面,然后接着返回左子树的根,所以lin后面不需要空格,直接再加上中序遍历根。最后要用空格加上rin。rin后面也自带空格。所以最后要删去那个多余的空格
#include <iostream>
#include <string>
using namespace std;
const int N = 40;
int pre[N], post[N];
//求树的构造方案
int dfs(int l1, int r1, int l2, int r2, string& in) //加&是要一直使用in
{
//跳出递归的条件
if (l1 > r1) return 1; //当树为空,一定成立
if (pre[l1] != post[r2]) return 0; //当前序与后序的根不同,二叉树不存在
//枚举每一个左子树的长度
int cnt = 0; //构建的总方案数数量
for (int i = l1; i <= r1; i++)
{
string lin, rin; //lin:左子树的中序遍历,rin:右子树的中序遍历
//lcnt:左子树的方案数量
int lcnt = dfs(l1+1, i, l2, l2+i-l1-1, lin);
//l1+1去掉前序根节点, r2:减去前序的树长度
int rcnt = dfs(i+1, r1, l2+i-l1-1+1, r2-1, rin);
//r2-1去掉后序遍历的根节点, l1:减去左子树
//若左右子树都有方案数即能构建成功
if (lcnt && rcnt)
{
//中序遍历结果
in = lin + to_string(pre[l1]) + ' ' + rin;
cnt += lcnt * rcnt;
//if (cnt > 1) break; // 方案数>1,即二叉树的构造不唯一
}
}
return cnt;
}
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++) cin >> pre[i];
for (int i = 0; i < n; i++) cin >> post[i];
string in; //存放中序遍历的结果
int cnt = dfs(0, n-1, 0, n-1, in);
if (cnt > 1) puts("No");
else puts("Yes");
in.pop_back(); //注意要去掉最后一个空格
cout << in << endl;
return 0;
}