java 实现根据先序遍历与中序遍历重建二叉树

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

  二叉树节点的定义如下:

public class BinaryTreeNode {
    int value;
    BinaryTreeNode left;
    BinaryTreeNode right;
}

  在二叉树的前序遍历中,第一个数字总是二叉树的根结点;在二叉树的中序遍历中,根结点一般在序列的中间,左边是左子树的节点,右边是右子树的节点。

  

 

 

   如上图所示:前序遍历的第一个节点为根结点,由此可以知道这棵树的根结点为 1 ,在中序遍历序列中找到根结点 1 的位置,根据中序遍历的特点,在 1 左边的三个节点其左子树的点,在 1 右边的四个节点都是其右子树的节点。

  由于在中序遍历序列中,右 3 个数都是最左子树的节点,有四个数都是右子树的节点,反推到前序遍历我们知道,根结点后紧跟着的 3 个数是左子树的节点,最后的 4 个数是右子树的节点。

  现在将两个序列进行拆分,我们可以得到以 1 为根结点的左子树的前序遍历序列 {2,4,7}、中序遍历序列 {4,7,2} 和 右子树的前序遍历序列 {3,5,6,8)、中序遍历序列 {5,3,8,6)

  既然我们分别找到了左、右子树的前序遍历序列和中序遍历序列,我们可以用同样的方法分别构造出左子树和右子树。也就是说,我们可以通过遍历来完成这个问题。

  重建的二叉树结构如下:

    

  下面是遍历函数:(检查数据不正确时抛出异常)

    /**
     * 根据先序遍历数组和中序遍历数组得到当前子树的根结点
     * 
     * @param preorder 先序遍历数组
     * @param inorder  中序遍历数组
     * @param startPreorder 先序遍历数组的第一个数组元素下标
     * @param endPreorder 先序遍历数组的最后一个数组元素下标
     * @param startInorder 中序遍历数组的第一个数组元素下标
     * @param endInorder  中序遍历数组的最后一个数组元素下标
     * @return 当前子树的根结点
   * @throws DataErrorException 
   */ 
    public BinaryTreeNode constructCore(int[] preorder, int[] inorder, int startPreorder, int endPreorder, int startInorder, int endInorder) throws DataErrorException {
        // 创建当前节点,先序遍历数组的第一个节点就是该节点的值
        BinaryTreeNode rootNode = new BinaryTreeNode();
        int rootValue = preorder[startPreorder];
        rootNode.value = rootValue;
        rootNode.left = null;
        rootNode.right = null;
        // 判断该节点是否为所需构造二叉树中最后一个节点
        if (startPreorder == endPreorder) {
            if (startInorder == endInorder && rootValue == inorder[startInorder])
                return rootNode;
            else
                throw new DataErrorException("数据错误");
        }
        // 找到当前节点所在中序遍历数组中的位置 rootInorder
        int rootInorder = startInorder;
        while (inorder[rootInorder] != rootValue && rootInorder <= endInorder) {
            rootInorder++;
        }
        if (inorder[rootInorder] != rootValue && rootInorder == endInorder) {
            throw new DataErrorException("数据错误");
        }
        // 规划该节点的左子树
        // 验证该节点是否还有左子树,若有则创建该左子树,返回左子树的根结点
        int leftLenght = rootInorder - startInorder;// 该节点左子树的长度
        int leftEndPreorder = startPreorder + leftLenght;// 在先序遍历中该节点左子树所有节点的最后一个节点的位置下标
        if (leftLenght > 0) {
            rootNode.left = constructCore(preorder, inorder, startPreorder + 1, leftEndPreorder, startInorder,
                    rootInorder - 1);
        }

        // 规划该节点的右子树
        // 验证该节点是否还有右子树,若有则创建该右子树,返回右子树的根结点
        if (endInorder - rootInorder > 0) {
            rootNode.right = constructCore(preorder, inorder, leftEndPreorder + 1, endPreorder, rootInorder + 1,endInorder);
        }
        // 返回该节点
        return rootNode;
    }

  异常函数为:

public class DataErrorException extends Exception{
    public DataErrorException(String message) {
        super(message);
    }
}

  调用遍历函数的重构二叉树函数:

    /**
     * 根据先序遍历数组与中序遍历数组建立一颗二叉树
     * @param preorder   先序遍历数组
     * @param inorder     中序遍历数组
     * @param lenght     数组长度
     * @return 二叉树的根结点
     * @throws DataErrorException 
     */
    public BinaryTreeNode construct(int[] preorder, int[] inorder, int lenght) throws DataErrorException {

        if (lenght == 0 || preorder == null || inorder == null)
            return null;
        BinaryTreeNode root = constructCore(preorder, inorder, 0, lenght - 1, 0, lenght - 1);
        return root;
    }

  构建的二叉树可以用后序遍历检查其正确性:

    /**
     * 根据后序遍历读取二叉树
     * @param root
     */
    public void readByPostorder(BinaryTreeNode node) {
        if (node == null)
            return;
        readByPostorder(node.left);
        readByPostorder(node.right);
        System.out.print(node.value + " ");

    }
posted @ 2020-04-09 17:44  葡萄籽pp  阅读(684)  评论(0编辑  收藏  举报