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

  • 相关阅读:
    比特币节点同步问题
    Vue用axios跨域访问数据
    vue之vue-cookies安装和使用说明
    vuejs目录结构启动项目安装nodejs命令,api配置信息思维导图版
    使用以太坊智能合约实现面向需要做凭证的企业服务帮助企业信息凭证区块链化
    将任意文件写入以太坊区块的方法,把重要事件,历史事件,人生轨迹加密记录到区块链永久封存
    Linux下几种重启Nginx的方式,找出nginx配置文件路径和测试配置文件是否正确
    php小数加减精度问题,比特币计算精度问题
    Fabric架构:抽象的逻辑架构与实际的运行时架构
    国外互联网大企业(flag)的涨薪方式
  • 原文地址:https://www.cnblogs.com/juncaoit/p/9043431.html
Copyright © 2020-2023  润新知