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

本文介绍如何根据一棵树的中序遍历与后序遍历构造二叉树的方法,详细解析了构造过程,并提供了易于理解的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、题目描述

根据一棵树的中序遍历与后序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]

返回如下的二叉树:


二、思路

思路来源于图解构造二叉树之中序+后序,画得图是真心棒

前提

解决此问题的关键在于要很熟悉树的各种遍历次序代表的什么,最好能够将图画出来。本题解带你先进行中序遍历和后续遍历二叉树,然后再根据遍历结果将二叉树进行还原。

首先,来一棵树

然后再看树的遍历结果

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

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

如下图所示

树的还原过程描述
根据中序遍历和后续遍历的特性我们进行树的还原过程分析

  1. 首先在后序遍历序列中找到根节点(最后一个元素)
  2. 根据根节点在中序遍历序列中找到根节点的位置
  3. 根据根节点的位置将中序遍历序列分为左子树和右子树
  4. 根据根节点的位置确定左子树和右子树在中序数组和后续数组中的左右边界位置
  5. 递归构造左子树和右子树
  6. 返回根节点结束

树的还原过程变量定义
需要定义几个变量帮助我们进行树的还原

  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

听不明白没关系,看图就对了,计算图示如下

树的还原过程

 


三、代码实现

修改了原作者对变量的命名,现在小学二年级的弟弟都看懂了。

将中序遍历和后序遍历的数组抽离到类成员级别,可以有效的避免数组在多次递归中的复制操作,提升运行效率。

    public class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;

        TreeNode(int x) {
            val = x;
        }
    }

    //保存中序遍历数值与下标的映射关系
    Map<Integer, Integer> inOrderMap = new HashMap<>();
    //后序遍历数组
    int[] postOrderArray;

    public TreeNode buildTree(int[] inorder, int[] postorder) {
        for (int i = 0; i < inorder.length; i++) {
            inOrderMap.put(inorder[i], i);
        }
        postOrderArray = postorder;

        return build(0, inorder.length - 1, 0, postorder.length - 1);

    }

    /**
     * @param inOrderStart   中序遍历下标开始值
     * @param inOrderEnd     中序遍历下标结束值
     * @param postOrderStart 后序遍历下标开始值
     * @param postOrderEnd   后序遍历下标结束值
     * @return
     */
    public TreeNode build(int inOrderStart, int inOrderEnd, int postOrderStart, int postOrderEnd) {
        if (inOrderStart > inOrderEnd || postOrderStart > postOrderEnd) {
            return null;
        }
        //获取根节点
        int rootValue = postOrderArray[postOrderEnd];
        //根节点在中序遍历中的索引
        int rootIndex = inOrderMap.get(rootValue);
        //构造根节点
        TreeNode root = new TreeNode(rootValue);
        root.left = build(inOrderStart, rootIndex - 1, postOrderStart, postOrderStart + rootIndex - inOrderStart - 1);
        root.right = build(rootIndex + 1, inOrderEnd, postOrderStart + rootIndex - inOrderStart, postOrderEnd - 1);
        return root;
    }

该方案需要访问长度为n的map与数组,且节点总数目也为n,因此时间复杂度为O(n)

空间复杂度,这边使用了长度为n的map与数组,且递归的平均深度(即树的高度)小于n,因此,空间复杂度为O(n)。

提交答案:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

SunAlwaysOnline

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值