首先来看今天的的一个题
就是已知一棵二叉树的前序和中序,求出二叉树的后序
刚好很巧的,今天一大早我就看了已知前序和中序,推算出二叉树的方法。
所以我的第一思路就是通过把二叉树还原回来再来一次后序遍历得出最终结果。
但是,感觉有点麻烦,我们就是能不能就是,不还原二叉树就把他的后序遍历的结果给求出来呢?
我们借用题目给出的数据
中序:A B E D F C H G
前序:C B A D E F G H
前序的第一个肯定是根,再结合中序就能划分出这棵树的左子树和右子树
中序:A B E D F C H G (蓝色代表左子树,红色代表右子树)
按照后序遍历顺序,要先从左子树开始找
前序的第二个B,也是左子树的根,把左子树单独拉出来划分
中序:A B E D F (蓝色代表左子树,红色代表右子树)
前序的第三个字母A
此时已经无法划分了,按照后序遍历的顺序,A为后序遍历的第一个节点
后序:A
前序的第四个字母D,以此划分
中序:E D F (蓝色代表左子树,红色代表右子树)
前序的第五个字母E,无法划分,作为后序遍历的第二个节点
后序:A E
前序的第六个字母F,无法划分,作为后序遍历的第三个节点
后序:A E F
此时根为D所在的左右子树都已经访问完了,所以作为后序遍历的第四个节点
后序:A E F D
根为B所在的左右子树也都已经访问完了,所以同样作为后序遍历的第五个节点
后序:A E F D B
在次以前序的第七个字母G划分
中序: H G (蓝色代表左子树,红色代表右子树)
以前序的第八个字母H划分
无法划分,作为后序遍历的第六个节点
后序:A E F D B H
根为G所在的左右子树都已经访问完毕,所以作为后序遍历的第七个节点
后序:A E F D B H G
最后就是总的根C,作为后序遍历的第八个节点
后序:A E F D B H G C
和最终的答案一致
思路可行,实践搞起
#include<iostream>
#include<string.h>
using namespace std;
char mtree[100];//记录中序遍历
char stree[100];//记录前序遍历
char etree[100];//记录后序遍历
int len=0 ;//记录后续遍历的复原长度
int present=0;//present表示当前前序遍历的查找位置
int restore(int head,int end)
{
if(head>end||present==strlen(stree))
return 0;
//找出划分点(子树的根)
int i;
for(i=head;i<=end;i++)
if(mtree[i]==stree[present])
break;
present++;//每查找一次,就遍历下一个
restore(head,i-1);//先遍历左子树
restore(i+1,end);//再遍历右子树
etree[len++]=mtree[i];
return 1;
}
int main()
{
scanf("%s",mtree);
scanf("%s",stree);
restore(0,strlen(mtree)-1);
printf("%s",etree);
}
不出所望,直接秒杀
接下来后面一个题目
这个题目也是有两种方法,不建立二叉树和建立二叉树的
首先来说第一种,不建立二叉树要怎么做
给出的样例第一看看过去,第一个肯定是根,但是如果给出的第一个不是根呢??
这个也是完全的有可能的,写题目,总是要小心点为上
思考一下也不难知道,根 是只可能出现一次的,因为他没有前驱,只有后继
而其他的节点能作为一个根的同时,也能作为另一个节点的后继,也就是会出现两次
所以我们的第一目标就是找到根,这里的根也不需要怎么找就是第一个,
按照前序的顺序
根——左子树——右子树
所以前序遍历的第一个就是根:a
然后沿着左子树找,下一个就是b
继续沿着左子树找,下一个就是d
左子树没有了就返回上一个子树,找他的右子树,也就是i
右子树找完了再返回上一个子树,继续找右子树,也就是c
最后只有一个左孩子j;
因此最终答案就是a b d i c j
代码如下
#include<iostream>
using namespace std;
char tree[100][4];
int n;
char result[200]={0};//存放最终结果
int len=0;//记录结果的长度
int check(int root)
{
result[len++]=tree[root][0];
if(tree[root][1]!='*')
{
for(int i=0;i<n;i++)//查找左子树的位置
if(tree[i][0]==tree[root][1])
{
check(i);
break;
}
}
if(tree[root][2]!='*')
{
for(int i=0;i<n;i++)//查找右子树的位置
if(tree[i][0]==tree[root][2])
{
check(i);
break;
}
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
scanf("%s",tree[i]);
for(int j=0;j<3;j++)
{
result[tree[i][j]]++;//统计出现的个数
}
}
//定位根的位置
int head;
for(int i=0;i<200;i++)
{
if(result[i]==1)
head=i;
}
//从根开始遍历
for(int i=0;i<n;i++)
if(tree[i][0]==head)
{
check(i);
break;
}
for(int i=0;i<len;i++)
cout<<result[i];
}
最后也顺利通过
接下来就是第二种方法
建立二叉树
总的来说,方法思路其实也差不多
第一步肯定也是找根,然后沿着根的左孩子右孩子一路延伸
最终前序遍历输出结果
#include<iostream>
using namespace std;
typedef struct Tree
{
char data;
struct Tree *lchild;//左孩子
struct Tree *rchild;//右孩子
}tree;
char dtree[100][4];
int n;
tree *root=NULL;//根节点
int shadow[200];
tree *add(int data)
{
tree *infor=new tree;
infor->data=data;
infor->lchild=infor->rchild=NULL;
return infor;
}
int check(int present,tree *pre)//pre:双亲节点
{
if(dtree[present][1]!='*')//创建左孩子节点
{
tree *lchild=add(dtree[present][1]);
pre->lchild=lchild;//连接
// 查找左子树
for(int i=0;i<n;i++)
{
if(dtree[i][0]==lchild->data)
{
check(i,lchild);//继续遍历查找
break;
}
}
}
// 创建右孩子节点
if(dtree[present][2]!='*')
{
tree *rchild=add(dtree[present][2]);
pre->rchild=rchild;//连接
// 查找右子树
for(int i=0;i<n;i++)
{
if(dtree[i][0]==rchild->data)
{
check(i,rchild);//继续遍历查找
break;
}
}
}
}
int front(tree *present)
{
if(present==NULL)
return 0;
cout<<present->data;
front(present->lchild);//先从左孩子开始查找
front(present->rchild);
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
scanf("%s",dtree[i]);
for(int j=0;j<3;j++)
shadow[dtree[i][j]]++;
}
//定位根的位置
int head;
for(int i=0;i<200;i++)
if(shadow[i]==1)
{
head=i;
break;
}
root=add(head);//创建根节点
// 从根开始遍历
for(int i=0;i<n;i++)
if(dtree[i][0]==head)
{
check(i,root);
break;
}
//前序遍历出结果
front(root);
}
相对于第一种方法而言,需要的添加一些功能,比如说,新建节点,连接节点,就会麻烦一些
两者总体上的思路都是差不多的