剑指 Offer 07. 重建二叉树

题目描述

在这里插入图片描述

思路分析

挖掘前序中序遍历的特点
前序遍历的第一个节点是根节点,只要找到根节点在中序遍历中的位置,在根节点之前被访问的节点都位于左子树,在根节点之后被访问的节点都位于右子树,由此可知左子树和右子树分别有多少个节点。

由于树中的节点数量与遍历方式无关,通过中序遍历得知左子树和右子树的节点数量之后,可以根据节点数量得到前序遍历中的左子树和右子树的分界,因此可以进一步得到左子树和右子树各自的前序遍历和中序遍历,可以通过递归的方式,重建左子树和右子树,然后重建整个二叉树。

使用一个 Map 存储中序遍历的每个元素及其对应的下标,目的是为了快速获得一个元素在中序遍历中的位置。调用递归方法,对于前序遍历和中序遍历,下标范围都是从 0 到 n-1,其中 n 是二叉树节点个数。

递归方法的基准情形有两个:判断前序遍历的下标范围的开始和结束,若开始大于结束,则当前的二叉树中没有节点,返回空值 null。若开始等于结束,则当前的二叉树中恰好有一个节点,根据节点值创建该节点作为根节点并返回。

若开始小于结束,则当前的二叉树中有多个节点。在中序遍历中得到根节点的位置,从而得到左子树和右子树各自的下标范围和节点数量,知道节点数量后,在前序遍历中即可得到左子树和右子树各自的下标范围,然后递归重建左子树和右子树,并将左右子树的根节点分别作为当前根节点的左右子节点。

代码展示

class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if (!preorder.size()) {//条件判断
            return nullptr;
        }
        TreeNode* root = new TreeNode(preorder[0]);//建立根节点
        stack<TreeNode*> stk;//建立栈存储节点
        stk.push(root);//入根节点
        int inorderIndex = 0;//中序遍历的的元素下标

        for (int i = 1; i < preorder.size(); ++i) {//遍历前序遍历数组
            int preorderVal = preorder[i];//保存当前的元素值
            TreeNode* node = stk.top();//获取栈顶元素

            if (node->val != inorder[inorderIndex]) {//栈顶元素的值不等于当前中序遍历的元素
                node->left = new TreeNode(preorderVal);//可以模拟前序遍历进行对左孩子的访问
                stk.push(node->left);//入栈
            }
            else {//找到了最左节点
                while (!stk.empty() && stk.top()->val == inorder[inorderIndex]) {
                    //相等val 说明是最右节点 需要弹栈并++index
                    node = stk.top();
                    stk.pop();//可pop是因为在push的时候已经进行过访问了
                    //每个节点在push的时候就对自身的val进行了存储但是需要结合前中进行right和left的访问
                    ++inorderIndex;
                }
                node->right = new TreeNode(preorderVal);
                stk.push(node->right);
            }
        }
        return root;
    }
};

结果分析

在这里插入图片描述
时间复杂度O(n)
空间复杂度O(n)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值