复原二叉树(二叉树遍历复原)
复原二叉树
http://codeup.cn/problem.php?cid=100000611&pid=0
题目简述:
给你一棵二叉树的前序遍历和中序遍历,要求输出这棵二叉树的后序遍历
输入无限组,没有停止要求(文件读入)
补充知识:
既然做到了这道题,那就补充一下关于还原二叉树及二叉树遍历的知识,如下:
- 二叉树遍历
1.前序遍历:按照“根、左、右”的顺序遍历一棵树
2.中序遍历:按照“左、根、右”的顺序遍历一棵树
3.后序遍历:按照“左、右、根”的顺序遍历一棵树
4.层序遍历:按层从根节点开始由上至下,每一层由左至右的顺序遍历一棵树
(层序遍历相当于对二叉树从根节点开始的BFS广度优先搜索)
- 根据遍历还原二叉树
1.知道前序和中序,树是唯一的。具体还原步骤如下:
(1)前序遍历的第一位是这棵树的Root(根节点)
(2)在中序遍历中找到Root的位置为index,在Root左边的即为Root的左子树,右边的则是右子树(特别的,如果没有元素在左边或右边,则说明该节点没有左子树或右子树)
(3)再在前序遍历中找到Root的下一位,即为Root左子树的根节点root(注意一下区别)
(4)然后像Root一样在中序中找到root的位置,在root左边的即为root的左子树,右边的则是右子树
(5)不断重复以上操作(其实重复1、2即可),一棵树就复原出来了
2.知道中序和后序,树是唯一的。具体步骤如下:
(1)因为后序是最后访问根,所以后序的最后一个节点是这棵树的Root
(后序处理其实可以看做将前序倒过来了。注意:只是操作像倒过来,并不是说前序反转就是后序!)
(2)在中序中找到Root的位置,以此划分左右子树
(3)然后重复1、2步骤,迭代递归求解即可复原树
PS:例题洛谷P1030:求先序排列
3.知道前序和后序,树不是唯一的。原因如下:
前序和后序在本质上都是将父节点与子节点进行分离,但并没有指明左子树和右子树的能力,因此得到这两个序列只能明确父子节点关系,而不能确定一棵二叉树。
来一波文字+图帮助大家理解qvq(以题目第二个输入为例)
4.知道中序和层序,树是唯一的。具体步骤如下:
(1)在层序遍历中循环中序遍历,每一次循环,找到一个根节点就输出,然后记录其在中序遍历中的位置t后直接break
(2)然后根据t来划分左右子树,然后再进入递归循环处理子树
PS:大家可以在草稿本上手模一遍,更好理解quq(虽本人在做题的时候对着草稿本想了1h,然而并没有什么用)
下面会给出根据中序+层序复原二叉树并输出前序遍历的例题和代码
总结一下:
知道前序中序或后序中序或层序中序,可以复原唯一的树;而只知道前序和后序和层序之中的两个或三个只能明确父子节点关系。
原理:前序或后序可以确立每一个子树的root,而中序则可以划分左右子树,然后迭代就可以复原一棵树
提示:
知道了原理,不妨自己先试着编一下代码,这样有助于理解下面给出的代码,也更能找出自己的错误qwq
代码Code:
因为这是模板题,所以思路就是如上补充的知识(如果不是很懂,可以根据下面代码进行理解,自认为很详细)
代码如下:
#include <bits/stdc++.h>
using namespace std;
char pre[100001];
char in[100001];
struct node {
char data; //数据域
node *lc; //指向左子树根节点的指针
node *rc; //指向右子树根节点的指针
};
inline void build(node * &t,int prel,int prer,int inl,int inr) {
if(prel>prer) { //前序序列长度小于等于0时,直接返回
t=NULL;
return ;
}
t=new node(); //申请一个node型变量的地址空间,用来存放当前二叉树的根节点
t->data=pre[prel]; //节点权值为pre[prel]
int index;
for(index=inl;index<=inr;index++) {
if(in[index]==pre[prel]) break; //在中序序列中找到当前的根节点位置,存在index中
}
int numl=index-inl; //左子树的节点个数
build(t->lc,prel+1,prel+numl,inl,index-1); //分别递归左右子树
build(t->rc,prel+numl+1,prer,index+1,inr);
}
inline void postorder(const node *t) {
if(t==NULL) return ; //到达空树,递归边界
postorder(t->lc); //访问左子树
postorder(t->rc); //访问右子树
printf("%c",t->data);
}
int main() {
while(cin>>pre>>in) { //连续输入
node *root=NULL; //新建空根节点root
build(root,0,strlen(pre)-1,0,strlen(pre)-1); //开始复原二叉树
postorder(root); //根据复原的二叉树输出后序遍历
puts("");
memset(pre,'0',sizeof(pre)); //清空好习惯qwq
memset(in,'0',sizeof(in));
}
return 0;
}
中序+层序-->复原二叉树+前序
#include <bits/stdc++.h>
using namespace std;
int len;
char in[10001],layer[10001];
inline void preorder(int left,int right) {
int t;
bool flag=false;
for(register int i=0;i<len;i++) { //层序
for(register int j=left;j<=right;j++) { //中序
if(layer[i]==in[j]) {
cout<<in[j]; //找到一个就输出一个
//cout<<" "<<left<<","<<right<<" "<<j<<endl;
t=j;
flag=1;
break;
}
}
if(flag==1) break;
}
if(left<t) preorder(left,t-1); //划分左右子树
if(t<right) preorder(t+1,right);
}
int main() {
cin>>in>>layer;
len=strlen(in);
preorder(0,len-1);
return 0;
}