一:主题
1.题目
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。
假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
2.思路
在二叉树的前序遍历中,第一个数字总是树的根节点。在中序遍历中,树的根节点在序列的中间,左子树的节点的值位于根节点的左边,右子树节点的值位于根节点值的右边。
因此需要扫描中序遍历序列才能找到很结点的值,由此可以找到左子树的节点的个数和右子树节点的个数,然后在前序遍历序列中找到左子树的根节点,再到中序遍历序列中找到左子树的左子树和右子树。
依次递归。由于二叉树的构造本身就是用递归实现的,所以重建二叉树也用递归进行实现实很简单的。
3.程序
二叉树:
1 package com.jianke.it; 2 /** 3 * 二叉树节点 4 * @author dell 5 * 6 */ 7 public class BinaryTreeNode { 8 int value; 9 BinaryTreeNode left; 10 BinaryTreeNode right; 11 }
处理程序:
1 package com.jianke.it; 2 /** 3 * 1 4 * / 5 * 2 3 6 * / / 7 * 4 5 6 8 * / 9 * 7 8 10 * @author dell 11 * 12 */ 13 public class ReConstructBinaryTree { 14 15 public static void main(String[] args) { 16 int[] preOrder={1,2,4,7,3,5,6,8}; 17 int[] inOrder={4,7,2,1,5,3,8,6}; 18 BinaryTreeNode node=reConstruct(preOrder,inOrder); 19 printTree(node); 20 } 21 22 private static BinaryTreeNode reConstruct(int[] preOrder, int[] inOrder) { 23 if(preOrder==null||inOrder==null||preOrder.length!=inOrder.length||preOrder.length<1) 24 return null; 25 return construct(preOrder,0,preOrder.length-1,inOrder,0,inOrder.length-1); 26 } 27 /** 28 * 根据前序遍历和中序遍历构建二叉树 29 * @param preOrder 前序遍历序列 30 * @param ps 前序遍历开始位置 31 * @param pe 前序遍历结束位置 32 * @param inOrder 中序遍历序列 33 * @param is 中序遍历开始位置 34 * @param ie 中序遍历结束位置 35 * @return 构建的树的根节点 36 */ 37 private static BinaryTreeNode construct(int[] preOrder,int ps,int pe,int[] inOrder,int is,int ie) { 38 //开始位置大于结束位置说明已经处理到叶节点了 39 if(ps>pe) 40 return null; 41 ///前序遍历第一个数字为当前的根节点 42 int value=preOrder[ps]; 43 //index为根节点在中序遍历序列中的索引 44 int index=is; 45 while(index<=ie&&inOrder[index]!=value){ 46 index++; 47 } 48 System.out.println("当前各参数的数值为->ps:"+ps+" pe:"+pe+" is:"+is+" ie:"+ie+" index:"+index+" rootValue:"+value); 49 //如果在整个中序遍历中没有找到根节点说明输入的数据是不合法的 50 if(index>ie){ 51 throw new RuntimeException("invalid input"+index); 52 } 53 BinaryTreeNode node=new BinaryTreeNode(); 54 node.value=value; 55 //当前节点的左子树的个数为index-is 56 //左子树对应的前序遍历的位置在preOrder[ps+1,ps+index-is] 57 //左子树对应的中序遍历的位置在inOrder[is,index-1] 58 node.left=construct(preOrder,ps+1,ps+index-is,inOrder,is,index-1); 59 //当前节点的右子树的个数为ie-index 60 //右子树对应的前序遍历位置在preOrder[ps+index-is+1,pe] 61 //右子树对应的中序遍历位置在inOrder[index+1,ie] 62 node.right=construct(preOrder,ps+index-is+1,pe,inOrder,index+1,ie); 63 return node; 64 } 65 //中序遍历递归打印 66 public static void printTree(BinaryTreeNode node){ 67 if(node!=null){ 68 printTree(node.left); 69 System.out.print(node.value+" "); 70 printTree(node.right); 71 } 72 } 73 }
4.效果
二:知识点
1.二叉树的遍历
树的遍历顺序大体分为三种:前序遍历(先根遍历、先序遍历),中序遍历(中根遍历),后序遍历(后根遍历)。
2.图形
3.前序遍历
前序遍历:前序遍历可以记为根左右,若二叉树为空,则结束返回
前序遍历的规则:
(1)访问根节点
(2)前序遍历左子树
(3)前序遍历右子树
前序遍历的输出结果:ABDECF
4.中序遍历
中序遍历可以记为左根右,也就是说在二叉树的遍历过程中,首先要遍历二叉树的左子树,接着遍历根节点,最后遍历右子树。
中序遍历的规则:
(1)中序遍历左子树
(2)访问根节点
(3)中序遍历右子树
中序遍历的输出结果:DBEAFC
5.后序遍历
后序遍历可以记为左右根,也就是说在二叉树的遍历过程中,首先按照后序遍历的规则遍历左子树,接着按照后序遍历的规则遍历右子树,最后访问根节点。
在二叉树为空的时候,结束返回。
后序遍历二叉树的规则:
(1)后序遍历左子树
(2)后序遍历右子树
(3)访问根节点
后序遍历的输出顺序:DEBFCA
三:知道前序,中序,求后序遍历
1.图形
2.三种遍历
PreOrder: GDAFEMHZ
InOrder: ADEFGHMZ
PostOrder: AEFDHZMG
3.知道前两种求第三种遍历
第一步,root最简单,前序遍历的第一节点G就是root。
第二步,继续观察前序遍历GDAFEMHZ,除了知道G是root,剩下的节点必然是root的左右子树之外,没法找到更多信息了。
第三步,那就观察中序遍历ADEFGHMZ。其中root节点G左侧的ADEF必然是root的左子树,G右侧的HMZ必然是root的右子树。
第四步,观察左子树ADEF,左子树的中的根节点必然是大树的root的leftchild。在前序遍历中,大树的root的leftchild位于root之后,所以左子树的根节点为D。
第五步,同样的道理,root的右子树节点HMZ中的根节点也可以通过前序遍历求得。在前序遍历中,一定是先把root和root的所有左子树节点遍历完之后才会遍历右子树,并且遍历的右子树的第一个节点就是右子树的根节点。
如何知道哪里是前序遍历中的左子树和右子树的分界线呢?通过中序遍历去数节点的个数。
在上一次中序遍历中,root左侧是A、D、E、F,所以有4个节点位于root左侧。那么在前序遍历中,必然是第1个是G,第2到第5个由A、D、E、F过程,第6个就是root的右子树的根节点了,是M。
第六步,观察发现,上面的过程是递归的。先找到当前树的根节点,然后划分为左子树,右子树,然后进入左子树重复上面的过程,然后进入右子树重复上面的过程。最后就可以还原一棵树了。
第七步,其实,如果仅仅要求写后续遍历,甚至不要专门占用空间保存还原后的树。只需要稍微改动第六步,就能实现要求。仅需要把第六步的递归的过程改动为如下:
1 确定根,确定左子树,确定右子树。
2 在左子树中递归。
3 在右子树中递归。
4 打印当前根。