【剑指】通过前序,中序遍历来重建二叉树

本文介绍了一种使用前序和中序遍历数据重建二叉树的方法,通过递归算法确定根节点,并以此划分左右子树,最终实现整个二叉树的重构。

已知二叉树的前序,中序遍历来重建二叉树
好吧!既然是二叉树我们想到比较多的还是递归遍历,毕竟比较方便一点的。

  • 如果说前序和中序相比比较突出的一个特点就是根节点在数组下标当中的位置吧,毫无疑问,前序遍历根节点在数组首元素的位置这应该特点,我们剩下的重建二叉树完全是可以向侦探小说一样顺藤摸瓜还原真相了

好吧看代码

	/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *   TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> in) {
        
        int inlen=in.size();
  
            if(inlen==0)
  
                return NULL;
  
            vector<int> left_pre,right_pre,left_in,right_in;
  
            //创建根节点,根节点肯定是前序遍历的第一个数
  
            TreeNode* head=new TreeNode(pre[0]);
  
            //找到中序遍历根节点所在位置,存放于变量gen中
  
            int gen=0;
  
            for(int i=0;i<inlen;i++)
  
            {
  
                if (in[i]==pre[0])
  
                {
  
                    gen=i;
  
                    break;
  
                }
  
            }
  
            //对于中序遍历,根节点左边的节点位于二叉树的左边,根节点右边的节点位于二叉树的右边
  
            //利用上述这点,对二叉树节点进行归并
  
            for(int i=0;i<gen;i++)
  
            {
  
                left_in.push_back(in[i]);
  
                left_pre.push_back(pre[i+1]);//前序第一个为根节点
  
            }
  
            for(int i=gen+1;i<inlen;i++)
  
            {
  
                right_in.push_back(in[i]);
  
                right_pre.push_back(pre[i]);
  
            }
  
            //和shell排序的思想类似,取出前序和中序遍历根节点左边和右边的子树
  
            //递归,再对其进行上述所有步骤,即再区分子树的左、右子子数,直到叶节点
  
           head->left=reConstructBinaryTree(left_pre,left_in);
  
           head->right=reConstructBinaryTree(right_pre,right_in);
  
           return head;
  
        }
        
            };
           
<think>嗯,用户问的是如何用前序和中重建二叉树。我得先回忆一下二叉树历的知识。前序历是根节点先访问,然后是左子树,再右子树。中历则是左子树先,根节点中间,右子树最后。所以重建的关键在于找到根节点,然后分割左右子树。 首先,前序数组的第一个元素就是根节点。然后在中数组中找到这个根节点的位置,左边就是左子树的中,右边是右子树的中。根据左子树的长度,可以在前序数组中确定左子树的前序部分和右子树的前序部分。然后递归处理左右子树。 比如,前序是[3,9,20,15,7],中是[9,3,15,20,7]。根是3,在中里找到3的位置,左边9是左子树,右边15,20,7是右子树。前序中左子树部分应该是接下来的1个元素(因为左中长度是1),也就是9,剩下的20,15,7是右子树的前序。然后递归构建左右子树。 需要注意的点是,如何处理数组的索引。比如,确定左子树在前序中的起始和结束位置。还有递归终止条件,当数组为空时返回nullptr。 另外,可能要考虑元素重复的情况,但题目一般假设没有重复。如果有重复的话,可能无法唯一确定树的结构。所以代码中需要假设元素唯一。 代码方面,可以用哈希表来存储中的值和索引,方便快速查找根的位置。递归函数参数包括前序的当前根索引,中的左右边界。每次递归时,前序的根索引递增,比如用引用或指针来维护,或者在每次递归时计算新的根位置。比如,前序根的位置应该是当前根的下一个位置,左子树的根是当前根+1,右子树的根是当前根+左子树长度+1。 例如,递归函数可能的结构: TreeNode* build(vector<int>& pre, int& preIndex, unordered_map<int,int>& inMap, int inLeft, int inRight)。 在函数里,先取出前序的当前根值,创建节点。然后在中中找到该值的索引,分割左右子树。preIndex每次递归时递增,左子树处理完后,preIndex已经指向右子树的根的位置。 测试时需要注意各种情况,比如空树、单节点、只有左子树或右子树的情况。 可能的问题包括索引计算错误,比如左子树的长度应该是根在中中的位置减去左边界。递归终止条件是当inLeft > inRight时返回空指针。 综上,步骤大致是:哈希表中存中的值和索引,递归构建,每次确定根节点,分割左右子树,递归处理左右。关键点在于正确计算左右子树的索引范围。</think><< 使用前序和中重建二叉树的步骤如下: 1. **确定根节点**:前序历的第一个元素即为当前子树的根节点 2. **分割中数组**:在中历中找到根节点位置,左边为左子树,右边为右子树 3. **递归构建子树**:根据左右子树的节点数量,在前序历中分割对应的左右子树列 示例代码实现: ```cpp #include <vector> #include <unordered_map> using namespace std; struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} }; class Solution { public: TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { unordered_map<int, int> inMap; for (int i = 0; i < inorder.size(); i++) inMap[inorder[i]] = i; int preIndex = 0; return build(preorder, preIndex, inMap, 0, inorder.size()-1); } private: TreeNode* build(vector<int>& pre, int& preIndex, unordered_map<int,int>& inMap, int inLeft, int inRight) { if (inLeft > inRight) return nullptr; int rootVal = pre[preIndex++]; TreeNode* root = new TreeNode(rootVal); int inIndex = inMap[rootVal]; root->left = build(pre, preIndex, inMap, inLeft, inIndex-1); root->right = build(pre, preIndex, inMap, inIndex+1, inRight); return root; } }; ``` **实现原理**: 1. 使用哈希表存储中历的值与索引的对应关系,实现O(1)时间查找根节点位置 2. 前序历索引通过引用传递维护递归过程中的当前节点位置 3. 通过递归不断划分左右子树的范围: - 左子树范围:中起始位置到根节点位置-1 - 右子树范围:根节点位置+1到中结束位置 **时间复杂度**:O(n) 每个节点处理一次 **空间复杂度**:O(n) 哈希表存储中索引
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值