【每天一题】剑指 Offer 07. 重建二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。

例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:[3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7

题目分析:

前序遍历特点: 节点按照 [ 根节点 | 左子树 | 右子树 ] 排序,以题目示例为例:[ 3 | 9 | 20| 15 |7 ]
中序遍历特点: 节点按照 [ 左子树 | 根节点 | 右子树 ] 排序,以题目示例为例:[ 9 | 3 | 15 |20 |7 ]
根据题目描述输入的前序遍历和中序遍历的结果中都不含重复的数字,其表明树中每个节点值都是唯一的。

根据以上特点,可以按顺序完成以下工作:

前序遍历的首个元素即为根节点 root 的值;
在中序遍历中搜索根节点 root 的索引 ,可将中序遍历划分为 [ 左子树 | 根节点 | 右子树 ] 。
根据中序遍历中的左(右)子树的节点数量,可将前序遍历划分为 [ 根节点 | 左子树 | 右子树 ] 。
自此可确定 三个节点的关系 :

  1. 树的根节点、
  2. 左子树根节点
  3. 右子树根节点(即前序遍历中左(右)子树的首个元素)。

子树特点: 子树的前序和中序遍历仍符合以上特点,以题目示例的右子树为例:前序遍历:[20 | 15 | 7],中序遍历 [ 15 | 20 | 7 ] 。

根据子树特点,我们可以通过同样的方法对左(右)子树进行划分,每轮可确认三个节点的关系 。此递推性质让我们联想到用 递归方法 处理。

在这里插入图片描述
伪代码

递推参数: 前序遍历的左边界preleft、前序遍历的右边界preRight、中序遍历左边界inleft、中序遍历右边界inRight。
终止条件: 当preleft>preRight || inleft>inRight ,子树中序遍历为空,说明已经越过叶子节点,此时返回 null 。
递推工作:
	建立根节点root: 值为前序遍历中索引为pre_root的节点值。
	搜索根节点root在中序遍历的索引i: 为了提升搜索效率,本题解使用哈希表 dic 预存储中序遍历的值与索引的映射关系,每次搜索的时间复杂度为 O(1)。
	构建根节点root的左子树和右子树: 通过调用 myBuildTree() 方法开启下一层递归。
		左子树: 先序遍历中「从 左边界preleft+1 开始的 pIndex-inleft+preleft」右边界的元素就对应了中序遍历中「从 左边界inleft 开始到 根节点定位pIndex-1」的元素
		右子树: 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
返回值: 返回 root,含义是当前递归层级建立的根节点 root 为上一递归层级的根节点的左或右子节点。
class Solution {
    public TreeNode myBuildTree(int[] preorder, int preleft, int preRight,
                                int[] inorder, int inleft, int inRight,
                                Map<Integer, Integer> map) {
        if(preleft>preRight || inleft>inRight){
            return null;
        }
         // 前序遍历中的第一个节点就是根节点
        int rootVal=preorder[preleft];
        // 先把根节点建立出来
        TreeNode root = new TreeNode(rootVal);
		 // 在中序遍历中定位根节点
        int pIndex=map.get(rootVal);
		// 递归地构造左子树,并连接到根节点
        // 先序遍历中「从 左边界preleft+1 开始的 pIndex-inleft+preleft」右边界的元素就对应了中序遍历中「从 左边界inleft 开始到 根节点定位pIndex-1」的元素
        root.left=myBuildTree(preorder,preleft+1,pIndex-inleft+preleft,
                inorder,inleft,pIndex-1,map);
        // 递归地构造右子树,并连接到根节点
        // 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
        root.right=myBuildTree(preorder,pIndex-inleft+preleft+1,preRight,
                inorder,pIndex+1,inRight,map);

        return root;
    }

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int preLen=preorder.length;
        int inLen=inorder.length;
        Map<Integer,Integer> map=new HashMap<>(preLen);
        // 构造哈希映射,帮助我们快速定位根节点
        for(int i=0;i<inLen;i++){
            map.put(inorder[i],i);
        }
        return myBuildTree(preorder,0,preLen-1,inorder,0,inLen-1,map);
    }

    public static void main(String[] args) {
        int[] preorder={3, 9, 8, 5, 4, 10, 20, 15, 7};
        int[] inorder={4, 5, 8, 10, 9, 3, 15, 20, 7};
        Solution solution=new Solution();
        System.out.println( solution.buildTree(preorder,inorder));
    }
}

时间复杂度:O(n)。对于每个节点都有创建过程以及根据左右子树重建过程。
空间复杂度:O(n)。存储整棵树的开销。

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

计忆芳华

制作不易,欢迎打赏

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

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

打赏作者

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

抵扣说明:

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

余额充值