剑指offer-重建二叉树---java实现

题目描述

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

或者再给出后序遍历序列{7,4,2,5,8,6,3,1},让你通过后序和中序重建二叉树。其实中序后序重建二叉树和前序中序重建二叉树原理相同,所以我们先来看前序中序重建二叉树。

重建结构为
                    1
                  /   \
                 2     3
                /     / \
               4     5   6
                \        /
                 7      8

前序中序重建二叉树 

  • 具体分析

前序中第一个元素总是二叉树的根节点,拿到根节点后遍历中序,寻找中序中的根节点,找到后根节点

左边的元素为根节点的左子树,右边的元素为右子树,例如上题中的前序preOrder = {1,2,4,7,3,5,6,8}和中序为inOrder =
{4,7,2,1,5,3,8,6} 。

  • 第一次:根节点为{1}
  1. 左子树为:preOrder = {2,4,7},inOrder = {4,7,2}
  2. 右子树为:preOrder = {3,5,6,8},inOrder = {5,3,8,6}
  • 第二次左子树:根节点为{2}
  1. 左子树为:preOrder = {4,7},inOrder = {4,7}
  2. 右子树为:null,因为inOrder = {2,4,7}根节点元素2右边没有节点
  • 第二次右子树:根节点为{3}
  1. 左子树为:{5},一个节点
  2. 右子树为:preOrder= {6,8},inOrder = {8,6}
  • 以此类推
  • 这部分写的好像不是很明白,如果看不懂直接从代码中的重要注释看。

代码中的重要注释

估计看着不方便,所以把代码中的重要注释贴在这里

这里很重要!!!肯定能看懂

  • 接着进行递归,因为得到中序的根节点后,中序数组根节点左边的元素为左子树,右边的元素为右子树,计算左右子树的起始和结束下标,然后继续进行递归
  • 计算左树子起始结束下标
  1. 前序起始位置startPre = 原来的前序的startPre后移一位,也就是 startPre = startPre + 1。
  2. 前序结束下标endPre = 前序起始位置 + 左子树元素的长度,左子树长度=查出的中序根节点下标值减去中序的初始下标,也就是左子树长度 = i - startIn,综合起来就是前序结束下标 enPre = startPre + i - startIn。
  3. 中序起始位置startIn = startIn ,保持不变。
  4. 前序结束下标endIn = 查出来的中序根节点下标值 - 1,即endIn = i-1。
  • 计算右子树起始结束下标
  1. 前序起始位置其实就是左子树前序结束下标+1,因为总的前序被分为两部分,一部分为左子树的前序, 一部分为右子树的前序,既然左子树的前序结束下标为endPre = startPre+i-startIn,那么右子树的起始下标startIn=startPre+i-startIn+1。
  2. 而右子树的结束下标也自然而然是endPre。
  3. 右子树的中序起始下标startIn同理,左右子树的中序也将总的中序分为两部分,而且处于查出来的中序根节点下标值的两边,即左子树结束下标为i-1,右子树起始下标为i+1。
  4. 而右子树的结束下标也自然而然是endIn。

代码实现

  二叉树数据结构定义:

class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val){
        this.val = val;
    }
}

  利用前序和中序重建二叉树:

代码中的重要注释也写在前面如果想测试最终重建成功与否,可以将重建的二叉树按层打印出来,按层打印二叉树可以看这篇博客https://mp.csdn.net/postedit/89322538

public static TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if (pre == null || in == null || pre.length != in.length || pre.length < 1) {
            return null;
        }
        //调用递归
        TreeNode root = reConstructBinaryTree(pre, 0, pre.length-1, in, 0, in.length-1);
        return root;
    }

    private static TreeNode reConstructBinaryTree(int[] pre, int startPre, int endPre, int[] in, int startIn, int endIn) {
        if (startPre > endPre || startIn > endIn) {
            return null;
        }
//        根节点为前序数组的第一个元素
        TreeNode root = new TreeNode(pre[startPre]);
        for (int i = 0; i <= endIn; i++) {
            //根据前序遍历得到的根结点在中序遍历中查找根结点的下标i
            if (pre[startPre] == in[i]) {
                /*
                * 接着进行递归,因为得到中序的根节点后,中序数组根节点左边的元素为左子树,右边的元素为右子树
                * 计算左右子树的起始和结束下标,然后继续进行递归
                * */
                /*计算左树子节点其实结束下标
                * 前序起始位置startPre = 原来的前序的startPre后移一位,也就是 startPre = startPre + 1
                * 前序结束下标endPre = 前序起始位置 + 左子树元素的长度,左子树长度=查出的中序根节点下标值减去中序的初始下标,
                * 也就是左子树长度 = i - startIn,综合起来就是前序结束下标 enPre = startPre + i - startIn。
                *
                * 中序起始位置startIn = startIn ,保持不变
                * 前序结束下标endIn = 查出来的中序根节点下标值 - 1,即endIn = i-1。
                * */
                root.left = reConstructBinaryTree(pre,startPre+1,startPre+i-startIn,in,startIn,i-1);
                /*
                * 前序起始位置其实就是左子树前序结束下标+1,因为总的前序被分为两部分,一部分为左子树的前序,
                * 一部分为右子树的前序,既然左子树的前序结束下标为endPre = startPre+i-startIn,
                * 那么右子树的起始下标startIn=startPre+i-startIn+1
                * 右子树的前序结束下标endPre = endPre
                *
                * 右子树的中序起始下标startIn同理,左右子树的中序也将总的中序分为两部分,而且处于查出来的中序根节点下标值的两边,
                * 即左子树结束下标为i-1,右子树起始下标为i+1,而右子树的结束下标也自然而然是endIn。
                * */
                root.right = reConstructBinaryTree(pre,startPre+i-startIn+1,endPre,in,i+1,endIn);
//              在中序中找到根节点直接退出循环
                break;
            }
        }
        return root;
    }

中序后序重建二叉树

前面实现了前序中序重建二叉树,那么可以以此类推后序中序重建二叉树。以下为实现代码

public static TreeNode reConstructBinaryTree1(int [] post,int [] in) {
        if (post == null || in == null || post.length != in.length || post.length < 1) {
            return null;
        }
        //调用递归
        TreeNode root = reConstructBinaryTree1(post, 0, post.length-1, in, 0, in.length-1);
        return root;
    }

    private static TreeNode reConstructBinaryTree1(int[] post, int startPost, int endPost, int[] in, int startIn, int endIn) {
        if (startPost > startPost || startIn > endIn) {
            return null;
        }
//        根节点为前序数组的第一个元素
        TreeNode root = new TreeNode(post[endPost]);
        for (int i = endPost; i >= 0; i--) {
            //根据后序遍历得到的根结点在中序遍历中查找根结点的下标i
            if (post[endPost] == in[i]) {

                root.left = reConstructBinaryTree1(post,startPost,startPost+i-startIn-1,in,startIn,i-1);

                root.right = reConstructBinaryTree1(post,startPost+i-startIn,endPost-1,in,i+1,endIn);
                break;
            }
        }
        return root;
    }

 

你可能感兴趣的:(算法/数据结构)