通过先序遍历和中序遍历后的序列还原二叉树

当我们有一个

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

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

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

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

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

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

实现代码如下:

  1 package com.tree.traverse;
  2
  3 import java.util.ArrayList;
  4 import java.util.List;
  5
  6 /**
  7  * @author Caijh
  8  *
  9  * 2017年6月2日 下午7:21:10
 10  */
 11
 12 public class BuildTreePreOrderInOrder {
 13
 14     /**
 15      *              1
 16      *             /  17      *            3   5
 18      *           /      19      *          7       11
 20      *       /
 21      *      9
 22      */
 23     public static int treeNode = 0;//记录先序遍历节点的个数
 24     private List<Node> nodeList = new ArrayList<>();//层次遍历节点的队列
 25     public static void main(String[] args) {
 26         BuildTreePreOrderInOrder build = new BuildTreePreOrderInOrder();
 27         int[] preOrder = { 1, 3, 7, 9, 5, 11};
 28         int[] inOrder = { 9, 7, 3, 1, 5, 11};
 29
 30         treeNode = preOrder.length;//初始化二叉树的节点数
 31         Node root = build.buildTreePreOrderInOrder(preOrder, 0, preOrder.length - 1, inOrder, 0, preOrder.length - 1);
 32         System.out.print("先序遍历:");
 33         build.preOrder(root);
 34         System.out.print("\n中序遍历:");
 35         build.inOrder(root);
 36         System.out.print("\n原二叉树:\n");
 37         build.prototypeTree(root);
 38     }
 39
 40     /**
 41      * 分治法
 42      * 通过先序遍历结果和中序遍历结果还原二叉树
 43      * @param preOrder    先序遍历结果序列
 44      * @param preOrderBegin     先序遍历起始位置下标
 45      * @param preOrderEnd    先序遍历末尾位置下标
 46      * @param inOrder    中序遍历结果序列
 47      * @param inOrderBegin    中序遍历起始位置下标
 48      * @param inOrderEnd     中序遍历末尾位置下标
 49      * @return
 50      */
 51     public Node buildTreePreOrderInOrder(int[] preOrder, int preOrderBegin, int preOrderEnd, int[] inOrder, int inOrderBegin, int inOrderEnd) {
 52         if (preOrderBegin > preOrderEnd || inOrderBegin > inOrderEnd) {
 53             return null;
 54         }
 55         int rootData = preOrder[preOrderBegin];//先序遍历的第一个字符为当前序列根节点
 56         Node head = new Node(rootData);
 57         int divider = findIndexInArray(inOrder, rootData, inOrderBegin, inOrderEnd);//找打中序遍历结果集中根节点的位置
 58         int offSet = divider - inOrderBegin - 1;//计算左子树共有几个节点,节点数减一,为数组偏移量
 59         Node left = buildTreePreOrderInOrder(preOrder, preOrderBegin + 1, preOrderBegin + 1 + offSet, inOrder, inOrderBegin,inOrderBegin + offSet);
 60         Node right = buildTreePreOrderInOrder(preOrder, preOrderBegin + offSet + 2, preOrderEnd, inOrder, divider + 1, inOrderEnd);
 61         head.left = left;
 62         head.right = right;
 63         return head;
 64     }
 65     /**
 66      * 通过先序遍历找到的rootData根节点,在中序遍历结果中区分出:中左子树和右子树
 67      * @param inOrder    中序遍历的结果数组
 68      * @param rootData    根节点位置
 69      * @param begin    中序遍历结果数组起始位置下标
 70      * @param end    中序遍历结果数组末尾位置下标
 71      * @return return中序遍历结果数组中根节点的位置
 72      */
 73     public int findIndexInArray(int[] inOrder, int rootData, int begin, int end) {
 74         for (int i = begin; i <= end; i++) {
 75             if (inOrder[i] == rootData)
 76                 return i;
 77         }
 78         return -1;
 79     }
 80     /**
 81      * 二叉树先序遍历结果
 82      * @param n
 83      */
 84     public void preOrder(Node n) {
 85         if (n != null) {
 86             System.out.print(n.val + ",");
 87             preOrder(n.left);
 88             preOrder(n.right);
 89         }
 90     }
 91     /**
 92      * 二叉树中序遍历结果
 93      * @param n
 94      */
 95     public void inOrder(Node n) {
 96         if (n != null) {
 97             inOrder(n.left);
 98             System.out.print(n.val + ",");
 99             inOrder(n.right);
100         }
101     }
102     /**
103      * 还原后的二叉树
104      * 二叉数层次遍历
105      * 基本思想:
106      *     1.因为推导出来的二叉树是保存在Node类对象的子对象里面的,(类似于c语言的结构体)如果通过递归实现层次遍历的话,不容易实现
107      *     2.这里采用List队列逐层保存Node对象节点的方式实现对二叉树的层次遍历输出
108      *     3.如果父节点的位置为i,那么子节点的位置为,2i 和 2i+1;依据这个规律逐层遍历,通过保存的父节点,找到子节点。并保存,不断向下遍历保存。
109      * @param tree
110      */
111     public void prototypeTree(Node tree){
112         //用list存储层次遍历的节点
113         if(tree !=null){
114             if(tree!=null)
115                 nodeList.add(tree);
116             nodeList.add(tree.left);
117             nodeList.add(tree.right);
118             int count=3;
119             //从第三层开始
120             for(int i=3;count<treeNode;i++){
121                 //第i层第一个子节点的父节点的位置下标
122                 int index = (int) Math.pow(2, i-1-1)-1;
123                 /**
124                  * 二叉树的每一层节点数遍历
125                  * 因为第i层的最大节点数为2的i-1次方个,
126                  */
127                 for(int j=1;j<=Math.pow(2, i-1);){
128                     //计算有效的节点的个数,和遍历序列的总数做比较,作为判断循环结束的标志
129                     if(nodeList.get(index).left!=null)
130                         count++;
131                     if(nodeList.get(index).right!=null)
132                         count++;
133                     nodeList.add(nodeList.get(index).left);
134                     nodeList.add(nodeList.get(index).right);
135                     index++;
136                     if(count>=treeNode)//当所有有效节点都遍历到了就结束遍历
137                         break;
138                     j+=2;//每次存储两个子节点,所以每次加2
139                 }
140             }
141             int flag=0,floor=1;
142             for(Node node:nodeList){
143                 if(node!=null)
144                     System.out.print(node.val+" ");
145                 else
146                     System.out.print("# ");//#号表示空节点
147                 flag++;
148                 /**
149                  * 逐层遍历输出二叉树
150                  *
151                  */
152                 if(flag>=Math.pow(2, floor-1)){
153                     flag=0;
154                     floor++;
155                     System.out.println();
156                 }
157             }
158         }
159     }
160     /**
161      * 内部类
162      * 1.每个Node类对象为一个节点,
163      * 2.每个节点包含根节点,左子节点和右子节点
164      */
165     class Node {
166         Node left;
167         Node right;
168         int val;
169         public Node(int val) {
170             this.val = val;
171         }
172     }
173 }

运行结果:

最后逐层输出二叉树的基本思想:
* 1.因为推导出来的二叉树是保存在Node类对象的子对象里面的,(类似于c语言的结构体)如果通过递归实现层次遍历的话,不容易实现
* 2.这里采用List队列逐层保存Node对象节点的方式实现对二叉树的层次遍历输出
* 3.如果父节点的位置为i,那么子节点的位置为,2i 和 2i+1;依据这个规律逐层遍历,通过保存的父节点,找到子节点。并保存,不断向下遍历保存。

时间: 2024-12-04 17:47:52

通过先序遍历和中序遍历后的序列还原二叉树的相关文章

算法学习 - 表达树的建立(后缀表达式法),树的先序遍历,中序遍历,后序遍历

表达树就是根据后缀表达式来建立一个二叉树. 这个二叉树的每个叶子节点就是数,真祖先都是操作符. 通过栈来建立的,所以这里也会有很多栈的操作. 树的先序遍历,中序遍历,后序遍历的概念我就不讲了,不会的自行百度,不然也看不懂我的代码. 下面是代码: // // main.cpp // expressionTree // // Created by Alps on 14-7-29. // Copyright (c) 2014年 chen. All rights reserved. // #includ

根据二叉树的前序遍历和中序遍历重建二叉树

题目描述 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回. 1 /** 2 * Definition for binary tree 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(

二叉查找树的前序遍历,后序遍历和中序遍历互求算法模板

面试的痛 前几天去阿里面试,一时忘记了二叉树的前序遍历中序遍历和后序遍历的概念,已经想死了. 然后最近去腾讯面试,被问到怎么已知前序遍历/后序遍历 + 中序遍历,求后序遍历/前序遍历,看来这种问题很喜欢考. 其实这个问题想清楚了很简单,只要把这三个概念理解透彻就可以做出来了,比如前序遍历的第一个值一定是根节点,然后这个根节点对应到中序遍历里面,在中序遍历的这个值的两边的值,一定在以此节点为根节点的两个子树上,同理,后序遍历也一样. 已知前序遍历和后序遍历是不能求唯一的中序遍历树的. #inclu

已知二叉树的前序遍历、中序遍历或者中序遍历、后序遍历求二叉树结构的算法

二叉树中的前序遍历是先访问根结点,再访问左子树,右子树. 中序遍历是先访问左子树,再是根结点,最后是右子树. 后序遍历是先访问左子树,再是右子树,最后是根结点. 算法思路是先根据前序遍历的第一个结点或者后序遍历的最后一个结点,查找对应在中序遍历中的位置,就可以确定左子树包含的元素和右子树包含的元素,最后通过递归来实现就可以了. 二叉树的表示形式为 //二叉树的结构表示为 class TreeNode { int val; TreeNode left; TreeNode right; TreeNo

根据前序遍历和中序遍历求后序遍历

根据前序遍历和中序遍历求后序遍历 一道HULU的笔试题(How I wish yesterday once more) 假设有棵树,长下面这个样子,它的前序遍历,中序遍历,后续遍历都很容易知道. PreOrder:         GDAFEMHZ InOrder:            ADEFGHMZ PostOrder:       AEFDHZMG 现在,假设仅仅知道前序和中序遍历,如何求后序遍历呢?比如,已知一棵树的前序遍历是"GDAFEMHZ",而中序遍历是"AD

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

问题描述: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回. 思路: 在二叉树的前序遍历序列中,第一个数字总是树的根结点的值.但在中序遍历序列中,根结点的值在序列的中间,左子树的结点的值位于根结点的值的左边,而右子树的结点的值位于根结点的值的右边.因此我们需要扫描中序遍历序列,才能找到根结点的值. 如下图所示,

前序遍历和中序遍历重建二叉树

对于二叉树,在此我不做过多讲解,如有不懂,请参照一下链接点击打开链接 1.在此二叉树的定义: struct BinaryTreeNode     {         BinaryTreeNode<T> *_Left;         BinaryTreeNode<T> *_Right;         T _data;     public:         BinaryTreeNode(const T& x)             :_Left(NULL)       

已知二叉树的先序遍历和中序遍历序列求后序遍历序列

package algorithm01; import java.util.Scanner; /** * 给出先序遍历和中序遍历序列求出二叉树的后续遍历序列 * @author wxisme * */ public class ToReverse { public static void main(String[] args) { Scanner scan = new Scanner(System.in); String s1, s2; while(scan.hasNext()) { s1 =

已知二叉树的前序遍历和中序遍历,如何得到它的后序遍历?

对一棵二叉树进行遍历,我们可以采取3中顺序进行遍历,分别是前序遍历.中序遍历和后序遍历.这三种方式是以访问父节点的顺序来进行命名的.假设父节点是N,左节点是L,右节点是R,那么对应的访问遍历顺序如下: 前序遍历    N->L->R 中序遍历    L->N->R 后序遍历    L->R->N /***************************************************************************************