zoukankan      html  css  js  c++  java
  • 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 打印当前根。

  • 相关阅读:
    Java规约之方法设计
    JVM第一篇 JVM与Java体系结构
    初学者学习Java方法集
    初学者学习Java方法集
    1.Spring-Boot 静态文件和页默认放置位置
    2.Spring-Boot常用配置解释
    3.Spring-Boot核心@SpringBootApplication介绍
    4.Spring-Boot中基于Junit的单元测试
    vue学习笔记(一) ---- vue指令(v-for 和 key 属性)
    vue学习笔记(一) ----- vue指令(菜单列表案例)
  • 原文地址:https://www.cnblogs.com/juncaoit/p/9043431.html
Copyright © 2011-2022 走看看