从前序(后序)与中序遍历序列中构造二叉树

这篇博客介绍了如何通过递归和哈希表优化从给定的中序和后序(或前序)遍历构造二叉树的算法,减少数组复制和赋值带来的性能损耗。作者提供了Java和C++两种语言的实现,并强调了使用HashMap存储中序遍历的下标可以显著提高效率。
摘要由CSDN通过智能技术生成

一、题目内容

 

二、题目描述

        这两道题目都属于比较经典的构造二叉树的方法,相信大家都可以顺利的在纸上构造完毕。大概步骤如下(以中序+后序为例):

 

很明显使用递归可以最大程度简化代码量,它的步骤流程是:

(1)如果数组大小为0,直接返回NULL(递归退出条件)

(2)如果不为空,那么当前节点的值取后序数组的最后一个,记为value。

(3)找到后序数组最后一个值在中序数组的位置,记为mid_index。

(4)根据mid_index切割中序数组,mid_index左边的部分递归放在root.left中,右边同理

(5)因为中序数组和后序数组大小相同,以此为条件对后序数组也进行切割。

(6)递归调用左区间和右区间。

(如果是前序+中序,区别就在于当前节点值取的是前序的第一个数)

        由此,如果单纯写出代码的话是很容易的。如下:

class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        if(inorder.length==0||postorder.length==0)
            return null;
        return travel(inorder,postorder);
    }
    public TreeNode travel(int[] inorder, int[] postorder){
        // 如果数组大小为0,说明为空节点
        if(inorder.length==0)
            return null;
        // 否则,取出后序数组最后一个元素作为节点元素
        int last_value = postorder[postorder.length - 1];
        // 找到后序数组最后一个元素在中序数组的
        TreeNode root = new TreeNode(last_value);
        int mid_index = 0;
        for(int i = 0; i < postorder.length; i++){
            if(inorder[i] == last_value){
                mid_index = i;
                break;
            }
        }
        int []mid_left = new int[mid_index];
        for(int i = 0; i < mid_left.length; i++){
            mid_left[i] = inorder[i];
        }
        int []mid_right = new int[inorder.length - mid_index - 1];
        for(int i = 0; i < mid_right.length; i++){
            mid_right[i] = inorder[i + mid_left.length + 1];
        }
        int []last_left = new int[mid_left.length];
        int []last_right = new int [mid_right.length];
        for(int i = 0; i < last_left.length; i++){
            last_left[i] = postorder[i];
        }
        for(int i = 0; i < last_right.length; i++){
            last_right[i] = postorder[i + last_left.length];
        }
        root.left = travel(mid_left,last_left);
        root.right = travel(mid_right,last_right);
        return root;
    }
}

        上述代码使用Java写的,如果使用C++也可以使用vector来储存数据。但是如果大家使用这样的代码提交的话,虽然可以通过,但是效率极低。归根结底是大量的重复对数组的定义和赋值占用了大量的时间和空间。

        如果要对其进行简化的话,我觉得可以通过HashMap来解决(总之使用索引代替定义数组)。将中序数组放入HashMap中,key代表中序数组的值,value则是中序数组每个值的下标。这样,递归调用的时候就很方便了。

三、完整代码

// 中序+后序
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;
    }
}
 // 中序+前序
class Solution {
    HashMap<Integer,Integer> map = new HashMap<>();
    int[] post;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if(preorder.length == 0)
            return null;
        post = preorder;
        for(int i = 0; i < inorder.length; i++){
            map.put(inorder[i],i);
        }
        TreeNode root = buildTree(0,inorder.length-1,0,preorder.length-1);
        return root;
    }
    public TreeNode buildTree(int mid_left,int mid_right,int pre_left,int pre_right){
        if(mid_right < mid_left||pre_right<pre_left)
            return null;
        int root_value = post[pre_left];
        int mid_index = map.get(root_value);
        TreeNode root = new TreeNode(root_value);
        root.left = buildTree(mid_left,mid_index-1,pre_left+1,pre_left+mid_index-mid_left);
        root.right = buildTree(mid_index+1,mid_right,pre_left+mid_index-mid_left+1,pre_right);
        return root;
    }
}

 

 可见效果还是很好的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

少๑渊

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

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

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

打赏作者

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

抵扣说明:

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

余额充值