A1119
个人思路
本题考查树的基本操作+树的遍历+树的重构,这道题还是很有挑战性的,花了我一2个小时的时间。
拿到题目后人是懵逼的状态,以前只知道前序/后序和中序唯一确定一棵二叉树,但给出前序和后序,如何重构这棵树呢?
先来回顾前序/后序&中序如何唯一确定二叉树的:
- 前序/后序的作用是提供根节点
- 中序的作用是根据根节点的位置来划分左右子树的
- 递归重构二叉树的左右子树即可
因此根据遍历结果重构二叉树的关键在于: 明确根节点并划分左右子树
前序+后序如何确定一棵二叉树:关键在于借助后序遍历中的根节点前一个结点来划分左右子树
无法唯一确定一棵树的原因:后序序列中 根节点前一个结点的位置 与 前序序列中树的根节点位置 相邻,导致无法判断当前子树是左子树还是右子树(即无法划分左右子树)
举个例子,配合画图有助于理解
例:
pre[] :1 2 3 4 6 7 5
post[]: 2 6 7 4 5 3 1
唯一确定in[]: 2 1 6 4 7 3 5(可以先画图帮助理解)
前提:明确两个根节点的含义
- 四个参数:preL, preR, postL, postR
- 先序遍历:根,左,右;后序遍历:左,右,根
- pre[preL] == post[postR]均表示树的根节点
- post[postR - 1]表示右子树的根节点(即根节点前一个结点,由于后序的遍历顺序是左,右,根)
思路(以第一次递归迭代为例):
- 后序序列post[postR] == 1表示整棵树的根节点;post[postR - 1] == 3表示右子树的根节点
- 遍历前序序列,找到右子树根节点的位置,即“3”的下标(pre[2] == 3)
- 此时前序序列中,右子树的根节点与树的根节点之间存在其他节点,因此可以对左右子树进行划分,“3”之前的均为左子树,“3”之后包括“3”均为右子树,此时可以唯一确定一棵树(因为已经明确了根节点,并划分清楚了左右子树)
- 考虑,如果右子树的根节点与树的根节点相邻(中间无其他节点),此时还能唯一确定一棵树么?答案是不能了。若二者相邻,就无法判断“3”后面的节点(包括“3”在内)到底是挂在树的根节点的左子树上还是右子树上(将例子中的“2”抹掉画图试试)
个人思路代码(Bug)
#include <bits/stdc++.h>
using namespace std;
int N;
int pre[105], post[105];
struct Node{
int data;
int left, right;
}nodes[105];
int pos;
int newNode(int x)
{
nodes[pos].data = x;
nodes[pos].left = nodes[pos].right = -1;
return pos++;
}
//判断二叉树是否唯一,并返回左右子树划分位置的下标
int checkUnique(int preL, int preR, int postL, int postR)
{
//注意序列下标从0开始
int firRoot = pre[preL];//树的根节点,pre[0] = post[N - 1]
int secRoot = post[postR - 1];//获取后序序列根节点的前一个节点
if(pre[preL + 1] == secRoot)
return -1;
for(int i = preL + 2; i <= preR; ++i)
{
if(pre[i] == secRoot)
return i;
}
}
bool isUnique = true;//标记树是否唯一
int recreate( int preL, int preR, int postL, int postR)
{
//递归边界
if(preL > preR || postL > postR || preL < 0 || preR < 0 || postL < 0 || postR < 0 || preR >= N || postR >= N)
return -1;
int root = newNode(post[postR]);//根据后序获得根节点
int secroot = checkUnique(preL, preR, postL, postR);
if(secroot == -1)
{
isUnique = false;
nodes[root].left = recreate(preL + 1, preR, postL, postR - 1);
}
else
{
int numleft = secroot - preL - 1;//在pre[]中划分左右子树
nodes[root].left = recreate(preL + 1, preL + numleft, postL, postL + numleft - 1);
nodes[root].right = recreate(preL + 1 + numleft, preR, postL + numleft, postR - 1);
}
return root;
}
vector<int> in;
void inOrder(int root)
{
//递归边界
if(root == -1)
return;
inOrder(nodes[root].left);
in.push_back(nodes[root].data);
inOrder(nodes[root].right);
}
int main(int argc, char *argv[]) {
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]);
}
int root = recreate(0, N - 1, 0, N - 1);
if(isUnique == false)
printf("No\n");
else
printf("Yes\n");
inOrder(root);
for(int i = 0; i < in.size(); ++i)
{
printf("%d", in[i]);
if(i < in.size() - 1)
printf(" ");
else
printf("\n");
}
return 0;
}
上述代码漏洞
修改后,仍有三个测试点未通过
checkUnique()缺少最后的返回值报错
原因:当输入的树不满足checkUnique判断条件,且无法进入循环时,该函数没有返回值;如输入1个结点时,此时便无返回值;输入1个结点若返回-1,会遇到下面的bug
修改后的代码(Bug)
#include <bits/stdc++.h>
using namespace std;
int N;
int pre[105], post[105];
struct Node{
int data;
int left, right;
}nodes[105];
int pos;
int newNode(int x)
{
nodes[pos].data = x;
nodes[pos].left = nodes[pos].right = -1;
return pos++;
}
//判断二叉树是否唯一,并返回左右子树划分位置的下标
int checkUnique(int preL, int preR, int postL, int postR)
{
//注意序列下标从0开始
int firRoot = pre[preL];//树的根节点,pre[0] = post[N - 1]
int secRoot = post[postR - 1];//获取后序序列根节点的前一个节点
if(pre[preL + 1] == secRoot)
return -1;
for(int i = preL + 1; i <= preR; ++i)
{
if(pre[i] == secRoot)
return i;
}
return -1;
}
bool isUnique = true;//标记树是否唯一
int recreate( int preL, int preR, int postL, int postR)
{
//递归边界
if(preL > preR || postL > postR)
return -1;
int root = newNode(post[postR]);//根据后序获得根节点
int secroot = checkUnique(preL, preR, postL, postR);
/*int secroot = preL + 1;
for(; secroot <= preR; ++secroot)
{
if(pre[secroot] == post[postR - 1])
break;
}*/
if(secroot == -1)
{
isUnique = false;
nodes[root].right = recreate(preL + 1, preR, postL, postR - 1);
}
else
{
int numleft = secroot - preL - 1;//在pre[]中,secroot位置划分左右子树
nodes[root].left = recreate(preL + 1, preL + numleft, postL, postL + numleft - 1);
nodes[root].right = recreate(secroot, preR, postL + secroot - preL - 1, postR - 1);
}
return root;
}
vector<int> in;
void inOrder(int root)
{
//递归边界
if(root == -1)
return;
inOrder(nodes[root].left);
in.push_back(nodes[root].data);
inOrder(nodes[root].right);
}
int main(int argc, char *argv[]) {
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]);
}
int root = recreate(0, N - 1, 0, N - 1);
if(isUnique == false)
printf("No\n");
else
printf("Yes\n");
inOrder(root);
for(int i = 0; i < in.size(); ++i)
{
printf("%d", in[i]);
if(i < in.size() - 1)
printf(" ");
else
printf("\n");
}
return 0;
}
再次修复BUG
这次问题出在了recreate()的递归边界上,除了L > R作为边界,还有L == R,也要返回根节点
原因:checkUnique()无法处理preL == preR的情况,若输入的树只有一个结点,根据checkUnique()会返回-1,说明树不唯一,此时出错
注意:
- 当无法唯一确定一棵树时,将子树默认挂在左子树上或者右子树上均是对的
- 只要过程中出现一次根节点与根节点前一个节点相邻的情况,结果就不唯一
- 要注意行末要有换行,否则会出现格式错误
AC代码
#include <bits/stdc++.h>
using namespace std;
int N;
int pre[105], post[105];
struct Node{
int data;
int left, right;
}nodes[105];
int pos;
int newNode(int x)
{
nodes[pos].data = x;
nodes[pos].left = nodes[pos].right = -1;
return pos++;
}
//判断二叉树是否唯一,并返回左右子树划分位置的下标
int checkUnique(int preL, int preR, int postL, int postR)
{
//注意序列下标从0开始
int firRoot = pre[preL];//树的根节点,pre[0] = post[N - 1]
int secRoot = post[postR - 1];//获取后序序列根节点的前一个节点
if(pre[preL + 1] == secRoot)
return -1;
for(int i = preL + 1; i <= preR; ++i)
{
if(pre[i] == secRoot)
return i;
}
return -1;
}
bool isUnique = true;//标记树是否唯一
int recreate( int preL, int preR, int postL, int postR)
{
//递归边界
if(preL > preR || postL > postR)
return -1;
if(preL == preR)//递归边界
{
int root = newNode(post[postR]);
return root;
}
int root = newNode(post[postR]);//根据后序获得根节点
int secroot = checkUnique(preL, preR, postL, postR);
if(secroot == -1)
{
isUnique = false;
nodes[root].right = recreate(preL + 1, preR, postL, postR - 1);
}
else
{
int numleft = secroot - preL - 1;//在pre[]中,secroot位置划分左右子树
nodes[root].left = recreate(preL + 1, preL + numleft, postL, postL + numleft - 1);
nodes[root].right = recreate(secroot, preR, postL + secroot - preL - 1, postR - 1);
}
return root;
}
vector<int> in;
void inOrder(int root)
{
//递归边界
if(root == -1)
return;
inOrder(nodes[root].left);
in.push_back(nodes[root].data);
inOrder(nodes[root].right);
}
int main(int argc, char *argv[]) {
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]);
}
int root = recreate(0, N - 1, 0, N - 1);
if(isUnique == false)
printf("No\n");
else
printf("Yes\n");
inOrder(root);
for(int i = 0; i < in.size(); ++i)
{
printf("%d", in[i]);
if(i < in.size() - 1)
printf(" ");
else
printf("\n");
}
return 0;
}