通过先序遍历和中序遍历后的序列还原二叉树(实现方法)

当我们有一个

先序遍历序列:1,3,7,9,5,11

中序遍历序列:9,7,3,1,5,11

我们可以很轻松的用笔写出对应的二叉树。但是用代码又该如何实现?

下面我们来简单谈谈基本思想。

首先,先序遍历的顺序是根据 根-左孩子-右孩子 的顺序遍历的,那么我们可以率先确认的是先序遍历序列的第一个数就是根节点,然后中序遍历是根据 左孩子-根-右孩子 的顺序遍历的。我们通过先序遍历确认了根节点,那么我们只需要在中序遍历中找到根节点的位置,然后就可以很好地区分出,那些属于左子树的节点,那些是属于右子树的节点了。如下图:

我们确定数字1为根节点,然后根据中序遍历的遍历顺序确定,中序遍历序列中数字1的左边全部为左子树节点,右边全部为右子树。通过左子树节点的个数,得出先序遍历序列中从根节点往后的连续3个数是属于左子树的,剩下的为右子树。这样再在左右子树的序列中重复以上步骤,最终找到没有子节点为止。

实现代码如下:

package com.tree.traverse;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Caijh
 *
 * 2017年6月2日 下午7:21:10
 */

public class BuildTreePreOrderInOrder {

  /**
   *       1
   *       / \
   *      3  5
   *      /   \
   *     7    11
   *    /
   *   9
   */
  public static int treeNode = 0;//记录先序遍历节点的个数
  private List<Node> nodeList = new ArrayList<>();//层次遍历节点的队列
  public static void main(String[] args) {
    BuildTreePreOrderInOrder build = new BuildTreePreOrderInOrder();
    int[] preOrder = { 1, 3, 7, 9, 5, 11};
    int[] inOrder = { 9, 7, 3, 1, 5, 11};

    treeNode = preOrder.length;//初始化二叉树的节点数
    Node root = build.buildTreePreOrderInOrder(preOrder, 0, preOrder.length - 1, inOrder, 0, preOrder.length - 1);
    System.out.print("先序遍历:");
    build.preOrder(root);
    System.out.print("\n中序遍历:");
    build.inOrder(root);
    System.out.print("\n原二叉树:\n");
    build.prototypeTree(root);
  }

  /**
   * 分治法
   * 通过先序遍历结果和中序遍历结果还原二叉树
   * @param preOrder  先序遍历结果序列
   * @param preOrderBegin   先序遍历起始位置下标
   * @param preOrderEnd  先序遍历末尾位置下标
   * @param inOrder  中序遍历结果序列
   * @param inOrderBegin  中序遍历起始位置下标
   * @param inOrderEnd   中序遍历末尾位置下标
   * @return
   */
  public Node buildTreePreOrderInOrder(int[] preOrder, int preOrderBegin, int preOrderEnd, int[] inOrder, int inOrderBegin, int inOrderEnd) {
    if (preOrderBegin > preOrderEnd || inOrderBegin > inOrderEnd) {
      return null;
    }
    int rootData = preOrder[preOrderBegin];//先序遍历的第一个字符为当前序列根节点
    Node head = new Node(rootData);
    int divider = findIndexInArray(inOrder, rootData, inOrderBegin, inOrderEnd);//找打中序遍历结果集中根节点的位置
    int offSet = divider - inOrderBegin - 1;//计算左子树共有几个节点,节点数减一,为数组偏移量
    Node left = buildTreePreOrderInOrder(preOrder, preOrderBegin + 1, preOrderBegin + 1 + offSet, inOrder, inOrderBegin,inOrderBegin + offSet);
    Node right = buildTreePreOrderInOrder(preOrder, preOrderBegin + offSet + 2, preOrderEnd, inOrder, divider + 1, inOrderEnd);
    head.left = left;
    head.right = right;
    return head;
  }
  /**
   * 通过先序遍历找到的rootData根节点,在中序遍历结果中区分出:中左子树和右子树
   * @param inOrder  中序遍历的结果数组
   * @param rootData  根节点位置
   * @param begin  中序遍历结果数组起始位置下标
   * @param end  中序遍历结果数组末尾位置下标
   * @return return中序遍历结果数组中根节点的位置
   */
  public int findIndexInArray(int[] inOrder, int rootData, int begin, int end) {
    for (int i = begin; i <= end; i++) {
      if (inOrder[i] == rootData)
        return i;
    }
    return -1;
  }
  /**
   * 二叉树先序遍历结果
   * @param n
   */
  public void preOrder(Node n) {
    if (n != null) {
      System.out.print(n.val + ",");
      preOrder(n.left);
      preOrder(n.right);
    }
  }
  /**
   * 二叉树中序遍历结果
   * @param n
   */
  public void inOrder(Node n) {
    if (n != null) {
      inOrder(n.left);
      System.out.print(n.val + ",");
      inOrder(n.right);
    }
  }
  /**
   * 还原后的二叉树
   * 二叉数层次遍历
   * 基本思想:
   *   1.因为推导出来的二叉树是保存在Node类对象的子对象里面的,(类似于c语言的结构体)如果通过递归实现层次遍历的话,不容易实现
   *   2.这里采用List队列逐层保存Node对象节点的方式实现对二叉树的层次遍历输出
   *   3.如果父节点的位置为i,那么子节点的位置为,2i 和 2i+1;依据这个规律逐层遍历,通过保存的父节点,找到子节点。并保存,不断向下遍历保存。
   * @param tree
   */
  public void prototypeTree(Node tree){
    //用list存储层次遍历的节点
    if(tree !=null){
      if(tree!=null)
        nodeList.add(tree);
      nodeList.add(tree.left);
      nodeList.add(tree.right);
      int count=3;
      //从第三层开始
      for(int i=3;count<treeNode;i++){
        //第i层第一个子节点的父节点的位置下标
        int index = (int) Math.pow(2, i-1-1)-1;
        /**
         * 二叉树的每一层节点数遍历
         * 因为第i层的最大节点数为2的i-1次方个,
         */
        for(int j=1;j<=Math.pow(2, i-1);){
          //计算有效的节点的个数,和遍历序列的总数做比较,作为判断循环结束的标志
          if(nodeList.get(index).left!=null)
            count++;
          if(nodeList.get(index).right!=null)
            count++;
          nodeList.add(nodeList.get(index).left);
          nodeList.add(nodeList.get(index).right);
          index++;
          if(count>=treeNode)//当所有有效节点都遍历到了就结束遍历
            break;
          j+=2;//每次存储两个子节点,所以每次加2
        }
      }
      int flag=0,floor=1;
      for(Node node:nodeList){
        if(node!=null)
          System.out.print(node.val+" ");
        else
          System.out.print("# ");//#号表示空节点
        flag++;
        /**
         * 逐层遍历输出二叉树
         *
         */
        if(flag>=Math.pow(2, floor-1)){
          flag=0;
          floor++;
          System.out.println();
        }
      }
    }
  }
  /**
   * 内部类
   * 1.每个Node类对象为一个节点,
   * 2.每个节点包含根节点,左子节点和右子节点
   */
  class Node {
    Node left;
    Node right;
    int val;
    public Node(int val) {
      this.val = val;
    }
  }
}

运行结果:

最后逐层输出二叉树的基本思想:

* 1.因为推导出来的二叉树是保存在Node类对象的子对象里面的,(类似于c语言的结构体)如果通过递归实现层次遍历的话,不容易实现

* 2.这里采用List队列逐层保存Node对象节点的方式实现对二叉树的层次遍历输出

* 3.如果父节点的位置为i,那么子节点的位置为,2i 和 2i+1;依据这个规律逐层遍历,通过保存的父节点,找到子节点。并保存,不断向下遍历保存。

以上这篇通过先序遍历和中序遍历后的序列还原二叉树(实现方法)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • C++基于先序、中序遍历结果重建二叉树的方法

    本文实例讲述了C++基于先序.中序遍历结果重建二叉树的方法.分享给大家供大家参考,具体如下: 题目: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回. 实现代码: #include <iostream> #include <vector> #include <stack> using

  • C#使用前序遍历、中序遍历和后序遍历打印二叉树的方法

    本文实例讲述了C#使用前序遍历.中序遍历和后序遍历打印二叉树的方法.分享给大家供大家参考.具体实现方法如下: public class BinaryTreeNode { public BinaryTreeNode Left { get; set; } public BinaryTreeNode Right { get; set; } public int Data { get; set; } public BinaryTreeNode(int data) { this.Data = data;

  • 二叉树先根(先序)遍历的改进

    二叉树的特点:每个结点的度最大不能超过2,并且左右子树不能颠倒 二叉树的存储结构:下面采用链式存储进行阐述,堆排序算法(快速排序改进)采用的顺序存储结构的二叉树,先看如下结构体的存储方式 顺序存储: 复制代码 代码如下: /*二叉树的顺序存储*/#define  MAX_TREE_SIZE 100typedef  TElemType  SqBiTree[MAX_TREE_SIZE]; 链式存储: 复制代码 代码如下: /*二叉树的链式存储*/typedef struct BiTNode{ TEl

  • 探讨:C++实现链式二叉树(用非递归方式先序,中序,后序遍历二叉树)

    如有不足之处,还望指正! 复制代码 代码如下: // BinaryTree.cpp : 定义控制台应用程序的入口点.//C++实现链式二叉树,采用非递归的方式先序,中序,后序遍历二叉树#include "stdafx.h"#include<iostream>#include<string>#include <stack>using namespace std;template<class T>struct BiNode{ T data; 

  • C#非递归先序遍历二叉树实例

    本文实例讲述了C#非递归先序遍历二叉树的方法.分享给大家供大家参考.具体如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication5 { class Program { static void Main(string[] args) { Node treeRoo

  • 通过先序遍历和中序遍历后的序列还原二叉树(实现方法)

    当我们有一个 先序遍历序列:1,3,7,9,5,11 中序遍历序列:9,7,3,1,5,11 我们可以很轻松的用笔写出对应的二叉树.但是用代码又该如何实现? 下面我们来简单谈谈基本思想. 首先,先序遍历的顺序是根据 根-左孩子-右孩子 的顺序遍历的,那么我们可以率先确认的是先序遍历序列的第一个数就是根节点,然后中序遍历是根据 左孩子-根-右孩子 的顺序遍历的.我们通过先序遍历确认了根节点,那么我们只需要在中序遍历中找到根节点的位置,然后就可以很好地区分出,那些属于左子树的节点,那些是属于右子树的

  • PHP根据树的前序遍历和中序遍历构造树并输出后序遍历的方法

    本文实例讲述了PHP根据树的前序遍历和中序遍历构造树并输出后序遍历的方法.分享给大家供大家参考,具体如下: 先来看看前序遍历.中序遍历与后序遍历原理图: 根据树的前序遍历和中序遍历构造树并输出后序遍历代码如下: <?php class BinaryTreeNode{ public $m_value; public $m_left; public $m_right; } function ConstructCore($preorder,$inorder){ if(count($preorder)!

  • iOS中UIAlertView3秒后消失的两种实现方法

    一,效果图. 二,代码. - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. UIAlertView* alert = [[UIAlertView alloc]initWithTitle:nil message:@"此信息3秒后消失" delegate:nil cancelButtonTitle:nil ot

  • vue中data改变后让视图同步更新的方法

    前言 不久前天看到一个比较有趣的问题,vue中data改变后,如何让视图同步更新,搜索了一下,并没有发现解决问题的方法,只能从源码去找解决方法了. 原因 我们都知道,在vue中改变数据后,视图并不是同步更新的. 在vue实例初始化后,会将data设置为响应式对象,当我们执行this.xxx = 1时,会触发这个响应式对象的setter.在setter中,会触发更新,通知所有订阅了xxx的订阅者.但是这个触发更新并不是同步的,它会将所有的watcher都添加到一个队列,并在nextTick之后去更

  • linux中启动tomcat后浏览器无法访问的解决方法

    前言 不论是要启动,还是要关闭tomcat服务,都是要去到tomcat安装目录下的bin路径,当然你要是电脑关机自然是不需要的. 但最近在启动后发现了一个问题:Centos服务器,本地和服务器ip互ping正常(本机ping服务器,服务器ping本地),但是服务器上部署好tomcat后,本机却无法通过浏览器访问服务器tomcat的8080端口. 比如服务器公网ip为:123.123.123.123,启动tomcat后默认端口为8080,通过123.123.123.123:8080访问时,无法访问

  • Python利用前序和中序遍历结果重建二叉树的方法

    本文实例讲述了Python利用前序和中序遍历结果重建二叉树的方法.分享给大家供大家参考,具体如下: 题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字. 这道题比较容易,前序遍历的结果中,第一个结点一定是根结点,然后在中序遍历的结果中查找这个根结点,根结点左边的就是左子树,根结点右边的就是右子树,递归构造出左.右子树即可.示意图如图所示: 利用前序和中序遍历的结果重建二叉树 Python代码: # coding: utf-8 ''

  • PHP实现二叉树深度优先遍历(前序、中序、后序)和广度优先遍历(层次)实例详解

    本文实例讲述了PHP实现二叉树深度优先遍历(前序.中序.后序)和广度优先遍历(层次).分享给大家供大家参考,具体如下: 前言: 深度优先遍历:对每一个可能的分支路径深入到不能再深入为止,而且每个结点只能访问一次.要特别注意的是,二叉树的深度优先遍历比较特殊,可以细分为先序遍历.中序遍历.后序遍历.具体说明如下: 前序遍历:根节点->左子树->右子树 中序遍历:左子树->根节点->右子树 后序遍历:左子树->右子树->根节点 广度优先遍历:又叫层次遍历,从上往下对每一层依

  • C语言二叉树常见操作详解【前序,中序,后序,层次遍历及非递归查找,统计个数,比较,求深度】

    本文实例讲述了C语言二叉树常见操作.分享给大家供大家参考,具体如下: 一.基本概念 每个结点最多有两棵子树,左子树和右子树,次序不可以颠倒. 性质: 1.非空二叉树的第n层上至多有2^(n-1)个元素. 2.深度为h的二叉树至多有2^h-1个结点. 满二叉树:所有终端都在同一层次,且非终端结点的度数为2. 在满二叉树中若其深度为h,则其所包含的结点数必为2^h-1. 完全二叉树:除了最大的层次即成为一颗满二叉树且层次最大那层所有的结点均向左靠齐,即集中在左面的位置上,不能有空位置. 对于完全二叉

随机推荐