003 重建二叉树

一:主题

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 打印当前根。

原文地址:https://www.cnblogs.com/juncaoit/p/9043431.html