10 重建二叉树

0 引言

题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
struct TreeNode
{
  int val;
  TreeNode* left;
  TreeNode* right;
}

1 抽象问题具体化

举例1:

前序遍历序列:{1,2,3}

中序遍历序列:{2,1,3}

重建二叉树

举例2:

前序遍历序列:{1,2,4,7,3,5,6,8 }

中序遍历序列:{4,7,2,1,5,3,8,6 }

重建二叉树

2 具体问题抽象分析

先上图.

 1)前序遍历的第一个数是根结点,建立根结点;

 2)把中序遍历二分为左子树和右子树,根据中序遍历的结果将前序遍历二分,分别得到左子树的中序和前序遍历序列以及右子树的中序和前序遍历序列;

 3)重复1)和2).

 4) 一直到二分结果为空,停止遍历。

3 demo 

void getRoot(vector<int> pre,vector<int> vin, TreeNode*  myTree){
    if(pre.empty() || vin.empty()){
        myTree = NULL;
        return;
    }            
    myTree->val = pre[0];
    vector<int> leftTreePre;
    vector<int> rightTreePre;
    vector<int> leftTreeVin;
    vector<int> rightTreeVin;
    leftTreePre.clear(),   rightTreePre.clear(), leftTreeVin.clear(), rightTreeVin.clear();
    int rootPosition = -1;
    for(int i=0;i<vin.size();i++){
        if(vin[i] != pre[0])
            leftTreeVin.push_back(vin[i]);
        else{
            ++ rootPosition;
            break;
        }                
        ++ rootPosition;
    }
    for(int i = rootPosition + 1;i<vin.size();i++)
        rightTreeVin.push_back(vin[i]);
    for(int i=1;i< 1+leftTreeVin.size();i++)
        leftTreePre.push_back(pre[i]);
    for(int i=leftTreeVin.size()+1;i<pre.size();i++)
        rightTreePre.push_back(pre[i]);  

    myTree->left = new TreeNode();
    myTree->right = new TreeNode();           
    getRoot(leftTreePre, leftTreeVin, myTree->left);
    getRoot(rightTreePre, rightTreeVin, myTree->right);
}

TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {        
    if(pre.empty() || vin.empty())  // 输入检查
        return NULL; 
    TreeNode* myTree = new TreeNode();
    myTree->left = NULL;
    myTree->right = NULL;
    getRoot(pre,vin,myTree);
    return myTree;
}
测试:
void test(){
    vector<int> pre{1,2,4,7,3,5,6,8};
    vector<int> vin{4,7,2,1,5,3,8,6};
    TreeNode* root = reConstructBinaryTree(pre,vin);
    preVisitBinaryTree(root);
    DestroyTree(root);
}

结果:

最后所有的空结点被0填充,不符合题目要求,原因在于下面几行代码。

    // 分配内存
   myTree->left = new TreeNode(); myTree->right = new TreeNode();   // 发现结点无效之后置为NULL ,但是该操作是无效的; 另外,尝试写成 delete myTree; myTree = NULL; 发现也会出错,访问的时候还是可以访问到,出现了野指针 if(pre.empty() || vin.empty()){ myTree = NULL; return; }

未判断myTree左右子树有没有结点就给myTree->left和myTree->right分配了内存空间,然后尝试在判断完之后作出补偿,将无效空间置为NULL是不可行的。

在分配内存空间之前必须确认该结点是有效的。

4 代码优化 

   TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {        
        if(pre.empty() || vin.empty())  // 输入检查
            return NULL;         
        TreeNode* myTree = new TreeNode(pre[0]); 
        vector<int> leftTreePre;
        vector<int> rightTreePre;
        vector<int> leftTreeVin;
        vector<int> rightTreeVin;
        int rootPosition = -1;
        for(int i=0;i<vin.size();i++){
            if(vin[i] != pre[0])
                leftTreeVin.push_back(vin[i]);
            else{
                ++ rootPosition;
                break;
            }                
            ++ rootPosition;
        }
        for(int i = rootPosition + 1;i<vin.size();i++)
            rightTreeVin.push_back(vin[i]);
        for(int i=1;i< 1+leftTreeVin.size();i++)
            leftTreePre.push_back(pre[i]);
        for(int i=leftTreeVin.size()+1;i<pre.size();i++)
            rightTreePre.push_back(pre[i]);        
        myTree->left = reConstructBinaryTree(leftTreePre, leftTreeVin);
        myTree->right = reConstructBinaryTree(rightTreePre, rightTreeVin);
        return myTree;
    }

经过修改之后用3中的测试用例测试是可以通过的。

原文地址:https://www.cnblogs.com/ghjnwk/p/10024564.html