【LeetCode】106.从中序与后序遍历序列构造二叉树(含有递归参数少的解法)

题目地址:https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/

题目

  1. 从中序与后序遍历序列构造二叉树
    根据一棵树的中序遍历与后序遍历构造二叉树。
    注意:
    你可以假设树中没有重复的元素。
    例如,给出
    中序遍历 inorder = [9,3,15,20,7]
    后序遍历 postorder = [9,15,7,20,3]
    返回如下的二叉树:
 	3
   / \
  9  20
    /  \
   15   7

思路分析:

首先,来一棵树

在这里插入图片描述

然后再看树的遍历结果

在这里插入图片描述

根据中序和后序遍历结果还原二叉树

中序遍历和后续遍历的特性
首先来看题目给出的两个已知条件中序遍历序列和后序遍历序列 根据这两种遍历的特性我们可以得出两个结论

  1. 在后序遍历序列中,最后一个元素为树的根节点
  2. 在中序遍历序列中,根节点的左边为左子树,根节点的右边为右子树

如下图所示:
在这里插入图片描述

树的还原过程变量定义

需要定义几个变量帮助我们进行树的还原
1.HashMap memo 需要一个哈希表来保存中序遍历序列中,元素和索引的位置关系.因为从后序序列中拿到根节点后,要在中序序列中查找对应的位置,从而将数组分为左子树和右子树
2.int ri 根节点在中序遍历数组中的索引位置
3.中序遍历数组的两个位置标记 [is, ie],is是起始位置,ie是结束位置
4.后序遍历数组的两个位置标记 [ps, pe] ps是起始位置,pe是结束位置

位置关系的计算

在找到根节点位置以后,我们要确定下一轮中,左子树和右子树在中序数组和后续数组中的左右边界的位置。

  1. 左子树-中序数组 is = is, ie = ri - 1
  2. 左子树-后序数组 ps = ps, pe = ps + ri - is -1 (pe计算过程解释,后续数组的起始位置加上左子树长度-1 就是后后序数组结束位置了,左子树的长度 = 根节点索引-左子树)
  3. 右子树-中序数组 is = ri + 1, ie = ie
  4. 右子树-后序数组 ps = ps + ri - is, pe - 1

听不明白没关系,看图就对了,计算图示如下
在这里插入图片描述

树的还原过程

在这里插入图片描述

代码如下所示:

class Solution {

    HashMap<Integer,Integer> memo = new HashMap<>();
    int[] post;

    public TreeNode buildTree(int[] inorder, int[] postorder) {
        for(int i = 0;i < inorder.length; i++) memo.put(inorder[i], i);
        post = postorder;
        TreeNode root = buildTree(0, inorder.length - 1, 0, post.length - 1);
        return root;
    }

    public TreeNode buildTree(int is, int ie, int ps, int pe) {
        if(ie < is || pe < ps) return null;

        int root = post[pe];
        int ri = memo.get(root);

        TreeNode node = new TreeNode(root);
        node.left = buildTree(is, ri - 1, ps, ps + ri - is - 1);
        node.right = buildTree(ri + 1, ie, ps + ri - is, pe - 1);
        return node;
    }
}

难点分析:

这道题主要的难点就是在于数组边界的划分。需要我们自己把图画出来,一个个的把数组的范围确立并且用对应的变量表达。
还使用的哈希表来存放中序遍历,便于在之后找到根节点在中序遍历数组中的下标。

另外一种方法

这种方法不用确定后序遍历数组的边界。所以在递归时传递的参数比较好,但理解起需要费一些脑力。

后序是左–右--根的特点,所以后序遍历从后向前是根–右--左,故在递归时根本没有必要将后序遍历的数组界限传入,每次有节点加入就自减一就行,只要满足了先拿到根节点然后遍历右子树,再遍历左子树这个原则就Okay。

   Map<Integer, Integer> map = new HashMap<>();//存放中序的映射
    int pindex = 0;//后序的索引
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        pindex = postorder.length - 1;//因为二叉树的第一个节点对应后序的最后一个,从最后一个开始
        for (int i = 0; i < inorder.length; i++) {
            map.put(inorder[i], i);
        }
        return build(postorder, 0, inorder.length - 1);
    }

    /**
     *
     * @param postorder 后序链表
     * @param inl 中序的左边界
     * @param inr 中序的右边界
     * @return 二叉树节点
     */
    public TreeNode build(int[] postorder, int inl, int inr) {
        if (inl > inr) return null;
        TreeNode node = new TreeNode(postorder[pindex--]);//从最后一个开始初始化为TreeNode。
        //注意上面的postorder[pindex--],此处相当于postorder[pindex],pindex--;
        
        int index = map.get(node.val);//得到在中序的索引
        node.right = build(postorder, index + 1, inr);//先遍历右子树
        node.left = build(postorder, inl, index - 1);//再遍历左子树
        return node;
    }

猜你喜欢

转载自blog.csdn.net/weixin_43314519/article/details/106243691