重建二叉树

以下题目内容来自《剑指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;
    }
}

通过递归调用,通过前序遍历序列得知当前子树的根节点,再根据中序遍历得知以当前根节点为根的子树的左右子树中 的节点,一步一步的从子节点到子数再到左(右)子树。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只打杂的码农

你的鼓励是对我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值