【遍历序列构造二叉树】更复杂的递归解法,需要深入思考

一、题目

leetcode链接:
从前序与中序遍历序列构造二叉树
从中序与后序遍历序列构造二叉树

在这里插入图片描述

在这里插入图片描述

二叉树的前序、中序、后序、层序遍历:
前序遍历:根节点+左子树+右子树
中序遍历:左子树+根节点+右子树
后序遍历:左子树+右子树+根节点
详见该博文链接,解释的很清楚: link

二、递归解法

1、对于前序和中序遍历构造二叉树,我们可以发现一个特点就是根节点可以很容易获取,即前序数组的第一个值;
2、要使用递归解法还是需要想清楚边界条件和非边界条件;
3、非边界条件就是原问题怎么分解为子问题,要构造二叉树,那么可以先获取根节点,再获取左右子树各自的范围区间,这样起码知道了哪些区间可以再用来构造左右子树,于是子问题不就可以继续分解下去:获取根节点—>寻找左右子树的区间。而中序数组恰好提供了左右子树的区间,根节点左/右边的区间为该根节点对应的左/右子树;
4、对于非边界条件:可以这么想,子问题不断分解下去,左右子树的区间一定会变为0,此时就是边界条件,要返回空节点。
5、代码实现上有几个小细节需要注意:

  • 在前序数组中获取根节点的值以后,如何快速找到中序数组中根节点的位置:可以使用哈希表in_map,保存中序数组inorder的 值和位置索引 为 key和value;
  • 创建另一个函数作为递归函数,并把preleft, preright, inleft, inright作为参数传入,分别表示子树的前序左边界、前序右边界、中序左边界、中序右边界。
  • 在buildsubtree的递归函数中要使用引用传参,有两个好处:将引用作为函数的参数进行传递,可以实现对实参的直接操作和修改;避免在函数内部产生参数的副本,消耗内存,可以提高程序的效率,在处理大型对象时可以节省内存和时间开销。在这里主要是第二个作用,因为如果对于in_map不使用引用&,力扣提交时会报错:“超出时间限制”

6、第二个问题:后序和中序遍历构造二叉树也类似。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* buildsubtree(vector<int>& preorder, vector<int>& inorder, unordered_map<int, int>&in_map, int preleft, int preright, int inleft, int inright){
        //递归的边界条件:左右边界相遇说明遍历节点结束,返回空节点
        if (preleft > preright || inleft > inright){
            return nullptr;
        }
        int preroot = preorder[preleft]; //前序数组中的对应子树的根节点
        int inroot_idx = in_map[preroot]; //中序数组中对应根节点的下标索引值
        int leftsubtree_len = inroot_idx - inleft; //中序数组中的左子树的区间长度
        TreeNode* root = new TreeNode(preroot); //建立根节点
        //递归构造左子树:前序数组的范围为:左边界preleft+1 ~ preleft+左子树长度
        //中序数组的范围为:左边界inleft ~ 根节点索引inroot_idx - 1
        root->left = buildsubtree(preorder, inorder, in_map, preleft+1, preleft+leftsubtree_len, inleft, inroot_idx-1);
        //递归构造右子树:前序数组的范围为:左边界preleft+左子树长度+1 ~ 右边界preright
        //中序数组的范围为:根节点索引+1 ~ 右边界inright
        root->right = buildsubtree(preorder, inorder, in_map, preleft+leftsubtree_len+1, preright, inroot_idx+1, inright);
        
        return root;
    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        unordered_map<int, int> in_map;
        int n = inorder.size();
        for (int i = 0; i < n; i++){
            in_map[inorder[i]] = i;
        }
        return buildsubtree(preorder, inorder, in_map, 0, n-1, 0, n-1);
    }
};
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
private:
    unordered_map<int, int> in_map;
public:
    TreeNode* buildsubtree(vector<int>& inorder, vector<int>& postorder, int inleft, int inright, int postleft, int postright){
        // 边界条件为中序和后序数组中左右边界相遇
        if (inleft > inright || postleft > postright){
            return nullptr;
        }
        int post_root = postorder[postright]; // 后序数组中的右边界为根节点
        int inroot_idx = in_map[post_root]; // 中序数组中根节点的索引
        // 获取中序数组中右子树的长度
        int rightsubtree_len = inright - inroot_idx;
        TreeNode* root = new TreeNode(post_root); // 创建根节点
        // 递归构造左子树:中序数组的范围:左边界inleft ~ 根节点索引值-1
        // 后序数组的范围:左边界postleft ~ 右边界 - 右子树长度 - 1
        root->left = buildsubtree(inorder, postorder, inleft, inroot_idx-1, postleft, postright - rightsubtree_len - 1);
        // 递归构造右子树:中序数组的范围:根节点索引值+1 ~ 右边界
        // 后序数组的范围:右边界-右子树长度 ~ 右边界 - 1 ,因为右边界为根节点
        root->right = buildsubtree(inorder, postorder, inroot_idx+1, inright, postright - rightsubtree_len, postright - 1);
        return root;

    }

    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        int n = inorder.size();
        for (int i = 0; i < n; i++){
            in_map[inorder[i]] = i;
        }
        return buildsubtree(inorder, postorder, 0, n-1, 0, n-1);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值