以下题目内容来自《剑指Offer》
题目描述:输入某二叉树的前序遍历和中序遍历的结果,请重新构造出该二叉树。假设输入的前序遍历和中序遍历的结果中不包含重复的数字。例如输入的前序遍历序列为{1,2,4,7,3,5,6,8}和中序遍历为{4,7,2,1,5,3,6,8},则重建出二叉树并输出它的头结点。
在二叉树的前序遍历序列中,第一个数字总是树的根节点的值。但在中序遍历中,根节点的值在序列的中间,左子树的结点的值位于根节点的值的左边,而右子树的结点的值位于根节点的右边。因此我们需要扫描中序遍历序列,才能找到根节点的值。
如图所示,前序遍历序列的第一个数字1就是根节点的值。扫描中序遍历序列,就能确定根节点的值的位置。根据中序遍历的特点,在根节点的值1前面3个数字都是左子树结点的值,位于1后面的数字都是右子树结点的值。
由于中序遍历序列中,有3个数字是左子树结点的值,因此左子树总共有3个左子结点。同样,在前序遍历的序列中,根节点后面的3个数字就是3个左子树结点的值,再后面的所有数字都是右子树结点的值。这样我们就在前序遍历和中序遍历两个序列中,分别找到了左右子树对应的子序列。
既然我们已经分别找到了左、右子树的前序遍历序列和中序遍历序列,我们可以用同样的方法分别去构建左右子树。也就是说,接下来的事情可以用递归的方法去完成。
书中只给出了思路和C++代码,根据这个思路,我用java实现,代码如下:
package com.sOffer;
/**
* 根据前序遍历和中序遍历重建二叉树
* @author lhqj
*
*/
class TreeNode{
int val;
TreeNode leftNode;
TreeNode rightNode;
public TreeNode(int val){
this.val = val;
}
}
public class Main2 {
public static void main(String[] args) {
int[] preOrder={1,2,4,7,3,5,6,8};
int[] inOrder={4,7,2,1,5,3,8,6};
System.out.println(Construct(preOrder,inOrder).val);
}
private static TreeNode Construct(int[] preOrder,int[]inOrder){
if(preOrder == null || inOrder == null)
return null;
return ConstructCore(preOrder,0,preOrder.length - 1,inOrder,0,inOrder.length - 1);
}
private static TreeNode ConstructCore(int[]preOrder,int sPreIndex,int ePreIndex,int[]inOrder,int sInIndex,int eInIndex){
//前序遍历序列的第一个数字就是根节点的值
int rootVal = preOrder[sPreIndex];
TreeNode root = new TreeNode(rootVal);
root.leftNode = null;
root.rightNode = null;
if(sPreIndex == ePreIndex){//只有一个元素
if(sInIndex == eInIndex && preOrder[sPreIndex] == inOrder[sInIndex]){//
return root;
}else{
try {
throw new Exception ();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//在中序遍历中找到根节点的值,再分为左子树和右子树
int rootIndex = sInIndex;
while(rootIndex <= eInIndex && inOrder[rootIndex] != rootVal){
++rootIndex;
}
//中序最后一个就是根节点,那么这棵树是没有右子树的,那输入的参数有问题
if(rootIndex == eInIndex && inOrder[rootIndex]!=rootVal){
try {
throw new Exception ();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
int leftLen = rootIndex - sInIndex;//左子树的节点个数
int leftPreOrderEndIndex = sPreIndex + leftLen;//对应前序序列中的左子树节点位置
if(leftLen > 0){
//构建左子树
root.leftNode = ConstructCore(preOrder, sPreIndex + 1, leftPreOrderEndIndex,
inOrder, sInIndex, rootIndex - 1);
}
if(leftLen < ePreIndex - sPreIndex){//右子树还有
//构建右子树
root.rightNode = ConstructCore(preOrder, leftPreOrderEndIndex + 1,
ePreIndex, inOrder, rootIndex + 1, eInIndex);
}
return root;
}
}
通过递归调用,通过前序遍历序列得知当前子树的根节点,再根据中序遍历得知以当前根节点为根的子树的左右子树中 的节点,一步一步的从子节点到子数再到左(右)子树。