题目描述(引自剑指offer)
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如,输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并输出它的根结点。
菜鸡与大佬的对话
有一回大佬对菜鸡说道,“你学过树型结构么?”
菜鸡略略点一点头。
大佬说,“学过树型结构,……我便考你一考。二叉树,怎样遍历的?”
菜鸡想,讨饭一样的人,也配考我么?便回过脸去,不再理会。
大佬等了许久,很恳切的说道,“不能遍历罢?……我教给你,记着!这些算法应该记着。将来做程序员的时候,写代码要用。”
菜鸡暗想,我和程序员的等级还很远呢,而且菜鸡修炼坊里都能查到;又好笑,又不耐烦,懒懒的答他道,“谁要你教,不是沿着某条搜索路线依次访问二叉树中的每个结点么?”
大佬显出极高兴的样子,将两个手指敲着计算机,点头说,“对呀对呀!……二叉树有四样遍历方式,你知道么?”
菜鸡愈不耐烦了,努着嘴走远。
大佬刚用手指点了点键盘,想在编辑器上写代码,见菜鸡毫不热心,便又叹一口气,显出极惋惜的样子。
——改编自鲁迅先生的《孔乙己》
菜鸡修炼坊
二叉树的遍历方式 | 定义 |
前序遍历 | 根结点->左子树->右子树 |
中序遍历 | 左子树->根结点->右子树 |
后序遍历 | 左子树->右子树->根结点 |
层次遍历 | 顶层(根)->底层(叶子),同层左->右 |
题目分析
在菜鸡修炼坊学习了二叉树的四种遍历之后,菜鸡进行了认真地思考。菜鸡发现,上次修炼的题目,采用右子树->根结点->左子树的遍历方式比上述四种遍历方式更加合适,因此,不要被既有的几种定义所限制,只是个名字而已。
根据四种遍历方式的定义,结合题目,菜鸡发现,在不含重复数字的前提下,通过前序遍历序列和中序遍历序列确实可以重建二叉树,同理,通过后序遍历序列和中序遍历序列也可以重建二叉树。因为中序遍历可以根据根结点的位置将左右子树加以区分,而且这个过程是可递归的。基于这个原理,可以发现,通过前序遍历序列和后序遍历序列则不能重建二叉树。
在搞清楚了重建二叉树的原理之后,菜鸡决定用Java代码实现他的心路历程。
代码实现
// 二叉树的定义
public class TreeNode {
int value = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int value) {
this.value = value;
}
}
import java.util.Arrays;
public class Solution {
// 重建二叉树
public TreeNode reConstructBinaryTree(int[] preOrder, int[] inOrder) {
// 判断入参
if (preOrder == null || preOrder.length == 0) {
return null;
}
TreeNode root = new TreeNode(preOrder[0]);
int rootIndex = 0;
// 寻找中序遍历序列中根结点所在的位置
for (int i = 0; i < inOrder.length; i++) {
if (inOrder[i] == preOrder[0]) {
rootIndex = i;
break;
}
}
// 递归重建左子树
root.left = reConstructBinaryTree(Arrays.copyOfRange(preOrder, 1, rootIndex + 1), Arrays.copyOfRange(inOrder, 0, rootIndex));
// 递归重建右子树
root.right = reConstructBinaryTree(Arrays.copyOfRange(preOrder, rootIndex + 1, preOrder.length), Arrays.copyOfRange(inOrder, rootIndex + 1, inOrder.length));
// 返回根结点
return root;
}
}
经过修炼之后,菜鸡发现前序遍历,中序遍历和后序遍历是递归定义的,因此实现方式可以采用递归和非递归两种方式。经过一番研究学习,菜鸡发现这四种遍历方式都蕴含了非常深刻的思想,确实值得牢记。菜鸡突然开始后悔今天面对大佬时的表现,他决定痛改前非,继续虚心修炼……
相关链接