106. 从中序与后序遍历序列构造二叉树

106. 从中序与后序遍历序列构造二叉树

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

示例 1:

输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]
示例 2:

输入:inorder = [-1], postorder = [-1]
输出:[-1]

思考

此题是根据中序遍历和后序遍历结果构建原二叉树。

首先回顾

  • 中序遍历 左节点 中间节点 右节点
    • 特点: 根节点的左右分别是其左右子树节点
  • 后序遍历 左节点 右节点 中间节点
    • 特点:根是最后一个遍历的。

我们分析inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]

黄色代表根节点,紫色根节点的左子树,蓝色右子树
黄色代表根节点,紫色根节点的左子树,蓝色右子树

根据后序遍历结果可知3为根节点,然后查看中序遍历,可区分其左子树节点有9,其右子树节点有15,20,7。

后序遍历,如果该根节点有右子树,那么后序遍历序列根节点的前一个一定是根节点的右子树

  • 注意: 如何判断是否有右子树,根据中序遍历看根节点后方是否有数据,如果有即有右子树,没有就没有右子树。

算法步骤:
利用递归,来进行依次向下构建

递归需要传入参数 ToATree(TreeNode root, List<Integer> in, List<Integer> pos)

首先定义 left ,right用来表示该节点是否存在左右子树

  • 根据root.val 获取root的index
    • 如果 index == 0 ,即不存在左子树
    • 如果 index == in.size() ,即不存在右子树

其次定义rootLeft,rootRight为null(代表root的左右子树)

在这里插入图片描述

如果存在右子树 , rootRight = new TreeNode(pos.get(pos.size()-2));

eg: 即根节点3存在右子树,其右子树为后序遍历root 的索引前一个节点

如果存在左子树,

  • 如果根节点有右子树
    • 其实根据后序遍历我们也知道,根节点的左孩子,是根节点的左子树最后一个遍历的,所以在其右子树前方,即 15 7 20 前方,根据中序遍历去除 其右子树便可获取 9 3的后序遍历序列,便可构建root的左节点 rootLeft
  • 如果根节点没有右子树
    • 故在后序遍历其root的索引前方就是,root的左节点

接下来就是递归并切割集合

  public static TreeNode buildTree(int[] inorder, int[] postorder) {
        TreeNode root = new TreeNode(postorder[postorder.length-1]);
        List<Integer> in=new ArrayList<>();
        List<Integer> pos=new ArrayList<>();
        for (int i = 0; i < inorder.length; i++) {
            in.add(inorder[i] );
            pos.add(postorder[i]);
        }
        ToATree(root,in,pos);
        return root;
    }

    private  static void ToATree(TreeNode root, List<Integer> in, List<Integer> pos) {
        // 表示是否存在左右子树,初始化存在
        int left = 0;
        int right = 0;
        // 获取 root 在 前序数组的指针
        int rootInIndex = in.indexOf(root.val);
        int rootPosIndex = pos.indexOf(root.val);
        // 左子树
        if(rootInIndex == 0) left=-1;
        // 右子树
        if(rootInIndex == in.size()-1) right=-1;
        
        // 递归结束条件 该节点为叶子节点
        if(left==-1&&right==-1) return;

        TreeNode rootright = null;
        TreeNode rootleft = null;
        // 存在右子树
        if(right == 0){
            rootright = new TreeNode(pos.get(pos.size()-2));
        }
        // 存在左子树
        // posIndex 右子树的第一个节点 也可能没有
        int posIndex = 0;
        if(left == 0){
            // 如果存在右子树
            if(right == 0){
                int miniIndex = in.size();
                for (int i = rootInIndex + 1; i < in.size(); i++) {
                    int index = pos.indexOf(in.get(i));
                    if (index<miniIndex){
                        miniIndex = index;
                    }
                }
                posIndex = miniIndex;
            }
            // 如果不存在右子树
            else {
                posIndex = pos.size() - 1;
            }
            rootleft = new TreeNode(pos.get(posIndex - 1));
        }
        if(rootleft!=null){
            root.left = rootleft;
            if(right==0){
                ToATree(root.left,in.subList(0,rootInIndex),pos.subList(0,posIndex));
            }
            else {
                ToATree(root.left,in.subList(0,rootInIndex),pos.subList(0,rootPosIndex));
            }
        }
        if(rootright!=null){
            root.right = rootright;
            if(left==0){
                ToATree(root.right,in.subList(rootInIndex+1,in.size()),pos.subList(posIndex,pos.size()-1));
            }
            else {
                ToATree(root.right,in.subList(rootInIndex+1,in.size()),pos.subList(0,pos.size()-1));
            }
        }

    }

但是这个效率不高啊,我觉得是使用集合的原因吗

在这里插入图片描述

自己查看了别人的思路,发现自己获取后序遍历时 左子树 和 右子树的临近点时过于复杂,其实可以直接使用长度相减即可。其次直接在数组上直接操作,不使用集合操作。

在这里插入图片描述

看来还是有继续优化的空间,但是奈何本人实力就这了。虽然效率不高,但是我觉得这个最容易理解。

首先递归参数ToATreeInt(root,inorder,postorder,inleft,inright,posleft,posright)

  • 数组
  • 本次递归的数组边界

递归执行大概思路,为这个节点构建左右子树,然后继续对这个节点的左右子树构建子树。

所以结束条件是数组边界左边界等于右边界时 if(inleft == inright) return;

其次后面的原理与上面大致相同,不在赘述。

优化代码如下:

public static TreeNode buildTreeInt(int[] inorder, int[] postorder) {
        TreeNode root = new TreeNode(postorder[postorder.length-1]);
        int inleft = 0;
        int inright = inorder.length -1;
        int posleft = 0;
        int posright = inorder.length - 1;

        //
        ToATreeInt(root,inorder,postorder,inleft,inright,posleft,posright);
        return root;
    }

    private static void ToATreeInt(TreeNode root, int[] inorder, int[] postorder, int inleft, int inright, int posleft, int posright) {
        // 递归结束条件
        if(inleft == inright) return;
        // left 1 存在左子树 0 不存在左子树
        int left = 1;
        int right = 1;
        
        int inRootIndex = 0;
        int posRootIndex = posright;

        for( int i = inleft; i<=inright; i++){
            if(inorder[i] == root.val){
                inRootIndex = i;
                break;
            }
        }

        if (inRootIndex == inleft) left=0;
        if (inRootIndex == inright) right=0;

        TreeNode rootleft = null;
        TreeNode rootright = null;
        if(right == 1){
            rootright = new TreeNode(postorder[posRootIndex-1]);
        }
        // posIndex定义为 root节点的左孩子在后序序列的下一个节点
            // 当有右子树时,其下一个节点为后序序列的第一个root节点的右子树节点
            // 没有右子树时,其下一个节点为root节点
        int posIndex = 0;
        if(left == 1) {
            if(right == 1){
                int rightLength =inright - inRootIndex;
                posIndex = posRootIndex - rightLength;
            }
            else {
                posIndex = posRootIndex;
            }
            rootleft =new TreeNode(postorder[posIndex - 1]);
        }

        if(rootleft!=null){
            root.left = rootleft;
            if(right == 1){
                ToATreeInt(root.left,inorder,postorder,inleft,inRootIndex-1,posleft,posIndex-1);
            }else {
                ToATreeInt(root.left,inorder,postorder,inleft,inRootIndex-1,posleft,posright-1);
            }
        }
        if(rootright!=null){
            root.right=rootright;
            if(left==1){
                ToATreeInt(root.right,inorder,postorder,inRootIndex+1,inright,posIndex,posRootIndex-1);
            }
            else {
                ToATreeInt(root.right,inorder,postorder,inRootIndex+1,inright,posleft,posRootIndex-1);
            }
        }


    }
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值