leetcode#105. 从前序与中序遍历序列构造二叉树

题目链接

题目描述:

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

太经典了,不需要例子了~

解题思路

  还是给一个例子吧!

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]

  如果这是一道选择填空甚至应用题,我们会怎么做?

  1. 由前序遍历特点可知,根结点root是"排头兵",也就是3
  2. 根据root值找到中序遍历数组中的相应索引,这时由中序遍历特点可知,左子树位于根结点索引左侧,也就是9;右子树位于根结点索引右侧15,20,7
  3. 对左、右子树数组重复1、2步骤,直到数组为空

  这就是我们日常做题的步骤,不难发现,其中蕴含着分治和递归的思想。分治部分可以参考二分查找算法,其实没差太多~简而言之,就是利用前序遍历得到根结点值去划分中序遍历左右子树,而后按根、左、右顺序递归建立二叉树。下面说三个细节:

  1. 如何通过前序遍历的根结点值找到对应中序遍历索引?当然可以通过遍历中序数组查找到根结点值。不过,为了降低执行用时,本文采用unordered_map提前记录中序遍历键值对(亲自测试,从20ms降到12ms)。
  2. 对于划分左右子树区间以及递归结束条件的确定,参考二分查找的查找区间细节。注意本文的左右指针设置为l = 0, r = length,即[l...r)左闭右开区间。
  3. 如何确定先序遍历中根结点位置?(为什么自增就确定是下一个根结点?)一定要结合先序遍历本身特点(根结点是"排头兵"),以及我们构建二叉树的顺序(根、左、右)。这就涉及到递归的流程(考虑栈的特点),一定是等左子树建完再去建右子树。所以这样自增是么问题哒。如果是根右左建树,显然自增不管用了,得通过中序遍历数组找右子树"排头兵",忒麻烦了~~

代码

class Solution {
public:
    //分治思想,参考二分查找
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        //记录
        for(int i = 0; i < inorder.size(); i++)
            record.insert(make_pair(inorder[i],i));          //(结点值,结点索引)
        return build(preorder, inorder, 0, inorder.size());  //[l...r)左闭右开,用于递归结束条件
    }

    TreeNode* build(vector<int>& preorder, vector<int>& inorder, int l, int r){
        if(l == r)
            return NULL;
        
        int root_Val = preorder[root_Pre];         //从先序遍历数组中获取当前根结点值
        int index = record.find(root_Val)->second; //再从中序遍历数组中找到当前根结点的索引处
        root_Pre++;         //为什么自增就确定是下一个根结点?
                            //一定要结合先序遍历本身特点(根结点是"排头兵"),以及我们构建二叉树的顺序(根、左、右)
                            //这就涉及到递归的流程(考虑栈的特点),一定是等左子树建完再去建右子树。
                            //所以这样自增是么问题哒。如果是根右左建树,显然自增不管用了,得通过中序遍历数组找右子树"排头兵",忒麻烦了。。。
        TreeNode* root = new TreeNode(root_Val);   //建立根结点
        root->left = build(preorder, inorder, l, index);       //注意是[l...r)左闭右开哦!
        root->right = build(preorder, inorder, index + 1, r);

        return root;
    }

private:
    int root_Pre = 0;         //根结点在先序遍历的索引位置
    unordered_map<int,int> record;  //记录各结点在中序遍历数组的索引,用于通过根结点划分左右子树区域
};

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。
本人blog:https://zhengguanyu.github.io

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值