剑指offer-重建二叉树

汇总:剑指offer算法合集

题目

输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。

假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

在这里插入图片描述

示例 1:

Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]

示例 2:

Input: preorder = [-1], inorder = [-1]
Output: [-1]

限制:

0 <= 节点个数 <= 5000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof

解题思路

  • 前序遍历的特点:先根节点,然后左子树,再到右子树,中序遍历的特点:先左子树,然后根节点,再到右子树
  • 根据前序遍历的特点,我们可以知道哪个是根节点,可以在中序遍历中找到找到这个根节点,然后就可以划分出中序遍历中的左子树和右子树
  • 根据中序遍历中左子树和右子树的长度,又可以在前序遍历中划分出左右子树
  • 根据前序遍历划分出来的左右子树,又可以知道中序遍历中左右子树中的根节点
  • 重复上面的步骤,每次发现根节点就创建这个节点,利用分治的思想,最终就能构建成一颗完整的树
  • 以示例1为例:
    • 根据前序遍历数组 [3,9,20,15,7],可以知道根节点为[3],可以在中序遍历数组 [9,3,15,20,7]中以3为界划分出左子树[9]和右子树[15,20,7]
    • 划分出来的左子树[9]数量为1,右子树[15,20,7]数量为3,我们可以知道,在前序遍历数组 [3,9,20,15,7]中,第一个数[3]是根节点,根节点之后的1个数[9]为左子树,紧接着这个节点的后3个节点[20,15,7]为右子树
    • 前序遍历划分出来的左子树[9]的第一个节点[9]就是左子树的根节点,右子树[20,15,7]的第一个节点[20]就是右子树的根节点
    • 回到中序遍历划分的数组中,可以知道在中序遍历划分的数组中左子树只有一个节点[9],本身也是它的根节点,无法再划分,而右子树[15,20,7]中,[20]是根节点,因此可以继续划分左子树为[15],右子树为[7]
    • 这两个子树都只有一个节点,所以它们本身就是自己的根节点,无法再划分
    • 在上面的过程中,每次发现根节点就新建这个节点,重复上面的过程,最后就构建成了完整的树

复杂度分析

递归建立n个节点,因此时间复杂度为O(n),用HashMap保存n个数的映射关系,递归栈深度最深为n(树变成链表),综合起来空间复杂度为O(n)

代码实现

class Solution {
    //用HashMap来保存中序遍历数组中值和下标的映射关系
    private HashMap<Integer, Integer> rootMap = new HashMap<>();
    //全局引用一下,不用每次递归都传数组
    private int[] preorder;

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        this.preorder = preorder;
        //遍历中序数组,形成值和下标的映射关系,方面后面取值的下标位置
        for (int i = 0; i < inorder.length; i++) {
            rootMap.put(inorder[i], i);
        }
        //第一次根节点为前序数组的第0个数,递归整个中序数组长度,后续用分治缩减范围
        return buildNewTree(0, 0, inorder.length - 1);
    }

    private TreeNode buildNewTree(int root, int left, int right) {
        //如果left和right相等,说明只剩一个节点了,这时候创建完这个节点就结束了
        //所以下一次left > right后,直接返回null不做别的操作
        if (left > right) return null;
        //每次创建根节点
        TreeNode rootNode = new TreeNode(preorder[root]);
        //从中序数组中找到根节点所在的下标
        int rootIndex = rootMap.get(preorder[root]);
        //左子树的根节点就是当前根节点在前序数组中下标+1,范围是原来的左边界到根节点-1
        rootNode.left = buildNewTree(root + 1, left, rootIndex - 1);
        //右子树的根节点是当前根节点在前序数组中的下标 + 左子树的数量 + 1,范围是当前根节点+1到原来的右边界
        rootNode.right = buildNewTree(root + (rootIndex - left) + 1, rootIndex + 1, right);
        return rootNode;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Nbin_Newby

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

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

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

打赏作者

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

抵扣说明:

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

余额充值