二叉树的先序,中序,后序如何遍历,不在此多说了。直接看题目描述吧(题目摘自九度oj剑指offer面试题6):
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并输出它的后序遍历序列。
- 输入:
输入可能包含多个测试样例,对于每个测试案例,
输入的第一行为一个整数n(1<=n<=1000):代表二叉树的节点个数。
输入的第二行包括n个整数(其中每个元素a的范围为(1<=a<=1000)):代表二叉树的前序遍历序列。
输入的第三行包括n个整数(其中每个元素a的范围为(1<=a<=1000)):代表二叉树的中序遍历序列。
输出
对应每个测试案例,输出一行:
如果题目中所给的前序和中序遍历序列能构成一棵二叉树,则输出n个整数,代表二叉树的后序遍历序列,每个元素后面都有空格。
如果题目中所给的前序和中序遍历序列不能构成一棵二叉树,则输出”No”。
下面看一下解题思路:
二叉树的问题,我们首先想到递归思想,即大问题的解题思路和小问题的解决思路是一样的。先来分析二叉树的先序和中序遍历序列能给我们哪些有用的信息,首先先序遍历可以告诉我们二叉树的根节点(即先序遍历序列的第一个元素)。其次,我们可以通过查询得到二叉树的根节点在中序遍历序列中的位置(假设为L),那么中序遍历序列L之前的是左子树,之后的右子树。知道这两部分信息之后,我们在考虑后序遍历,对于一个二叉树来说,根节点是后序遍历的最后一个去遍历的节点。
经过以上分析,我们就可以这样解题了(如下图):
现在要考虑的是,先递归右子树还是左子树,因为后序遍历是先左后右,而我们现在是从后往前得到后序遍历序列,所以先递归右子树。还有一个问题,即什么时候不能构成二叉树,本人以为只要根节点在中序遍历序列中查找不到就不能构成二叉树。
题目就分析到这,下面给出C语言完整代码,在九度oj上已AC,代码如下:
#include<stdio.h>
#include<stdlib.h>
/*
*已知二叉树的先序和中序遍历,求后序遍历
*/
int pos[1000];
int n;
int flag=0;
int locationInMid(int mid[],int ms,int me,int h)
{
int i;
for(i=ms;i<=me;++i)
{
if(mid[i]==h)
return i;
}
return -1;
}
void postOrder(int pre[],int ps,int pe,int mid[],int ms,int me) //有待改进的地方,函数的参数可以简化,因为数组的地址是0号元素的地址,知道头结点在mid中的位置后,左右子树的个数即可得到
{
int hm=0,cl=0,cr=0;
pos[--n]=pre[ps];
//--n;
hm=locationInMid(mid,ms,me,pre[ps]);
if(hm==-1)
{
// printf("No
");
flag=-1;
return ;
}
else
{
cl=hm-ms;
cr=me-hm;
if(cr>0)
postOrder(pre,ps+cl+1,pe,mid,hm+1,me);
if(cl>0)
postOrder(pre,ps+1,ps+cl,mid,ms,hm-1);
}
return ;
}
int main()
{
int pre[1000];
int mid[1000];
int i,t;
while(scanf("%d",&n)!=EOF)
{
t=n;
for(i=0;i<n;++i)
scanf("%d",&pre[i]);
for(i=0;i<n;++i)
scanf("%d",&mid[i]);
postOrder(pre,0,n-1,mid,0,n-1);
if(flag==-1)
{
printf("No
");
flag=0;
}
else
{
for(i=0;i<t;++i)//注意:n是全局变量所以上一个语句执行完之后,n的值已经不是原来的n了,所以要用t保存n最初的值
printf("%d ",pos[i]);
printf("
");
}
}
return 0;
}
本题的代码并不是最简洁的,但最容易理解,其实ps,pe,ms,me四个变量并不完全需要,因为pre指向的pre[ps],而知道二叉树的节点个数和根节点在中序遍历中的位置即可求得左右子树的位置,大家可以思考一下,优化一下代码。