07 重建二叉树

题目描述:

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

测试用例:

1)普通二叉树(完全二叉树、不完全二叉树)

2)特殊二叉树(所有节点都没有右/左子节点的二叉树,只有一个节点的二叉树)

3)特殊输入测试(空树:根节点指针为nullptr,输入的前序遍历序列与中序遍历序列不匹配

解题思路:

1)根据前序遍历序列与中序遍历序列,不断地分割成左子树与右子树,利用递归思路求解。

 

代码:

1)

 1 class Solution {
 2 public:
 3     TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
 4         if(pre.size()!=vin.size())
 5             return NULL;
 6         if(pre.empty())
 7             return NULL;
 8         // body
 9         TreeNode* head = new TreeNode(pre[0]);
10         int rootNum = -1;
11         vector<int> subPreL,subVinL,subPreR,subVinR;
12         
13         for(int i=0;i<vin.size();i++){
14             if(vin[i]==pre[0]){
15                 rootNum =i;
16                 break;
17             }
18         }
19         //left
20         for(int i=0;i<rootNum;i++){
21             subPreL.push_back(pre[i+1]);
22             subVinL.push_back(vin[i]);
23         }
24         //right
25         for(int i=rootNum+1;i<vin.size();i++){
26             subPreR.push_back(pre[i]);
27             subVinR.push_back(vin[i]);
28         }
29         head->left=reConstructBinaryTree(subPreL,subVinL);
30         head->right=reConstructBinaryTree(subPreR,subVinR);
31         return head;
32     }
33 };
View Code

注意:「1」书写要认真,确定中序遍历根结点时,由于马虎(=写成==),编译一直显示:内存超限:您的程序使用了超过限制的内存

/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
*/
//把构建二叉树问题分解为构建左右子树问题
//注意编写程序时,一些异常判断
class Solution {
public:
    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        //TreeNode* tree;
        if ( pre.empty()||vin.empty()|| (pre.size()!=vin.size())){
            return NULL;
        }
        return reConstructSubTree( pre, vin);
    }
    TreeNode* reConstructSubTree(vector<int> pre,vector<int> vin){
        //创建根节点
        int rootVal = pre[0];
        TreeNode* root = new TreeNode(pre[0]);
        //使用下面的方法构建root结点,会报错段错误:
        //您的程序发生段错误,可能是数组越界,堆栈溢出(比如,递归调用层数太多)等情况引起
        //TreeNode rootNode = TreeNode(rootVal);
        //TreeNode* root = &rootNode; //& 给指针赋值,取地址

        //终止条件,只有一个根节点,直接返回
        if ((int(pre.size())==1)&& (int(vin.size())==1)) //只有一个元素,且两个元素相等
            if (pre[0]==vin[0])  //判断是否相等,不相等则输入有误
                 return root;
            else
                exit(1); //终止程序
        
        //还有其他结点,接着划分左右子树,再返回
        //在中序中寻找root结点位置
        vector<int> preTempL, vinTempL, preTempR, vinTempR;
        
        int rootVinIndex = -1;
        for(int i=0;i<int(vin.size());i++){ //ERROR:comparison of integers of different signs: 'int' and 'size_type'
            if(vin[i]==rootVal){
                rootVinIndex = i;
                break; //找到后,提前终止
            }
            //构建左子树
            preTempL.push_back(pre[i+1]);
            vinTempL.push_back(vin[i]);
        }
        if (rootVinIndex==-1)
            exit(1);  //没有找到相等的元素,说明输入有误
            //throw exception("Invalid input");
        //构建右子树
        for(int i=rootVinIndex+1;i<int(vin.size());i++){
            preTempR.push_back(pre[i]);
            vinTempR.push_back(vin[i]);
        }
        //preTempL.assign(pre.begin()+1,pre.begin()+rootVinIndex);//rootVinIndex=0时,发生段错误
        //vinTempL.assign(vin.begin(),vin.begin()+rootVinIndex-1);
        //preTempR.assign(pre.begin()+rootVinIndex+1,pre.end());
        //vinTempR.assign(vin.begin()+rootVinIndex+1,vin.end());

        //判断左右子树是否非空,非空--构建树
        if(!preTempL.empty())
            root->left = reConstructSubTree( preTempL, vinTempL);
        if(!preTempR.empty())
            root->right = reConstructSubTree( preTempR, vinTempR);
        
        return root;
    }

 遇到的问题:

「1」vector assign的使用:

  preTemp.assign(first,last);  其中:preTemp时要赋值的vector变量,而指针(first,last)指向要截取的vector变量。

       -- 赋值元素包含last所指的元素,即[first,last]而不是[first,last)

       -- assign(first,last) error 不给出要赋值的变量,是错误的

       -- last指针的地址应该大于first的地址,否则会报错。即不能逆向寻址。line59-62 使用错误的原因是,当rootVinIndex=0时,addr(last)=pre.begin()<addr(first)=pre.begin()+1,报错。因此使用for循环给新分割的子树vector向量赋值。

「2」根节点的创建 使用语句: TreeNode* root = new TreeNode(pre[0]);   new创建的变量在栈中保存,不会自动销毁,必须手动显示销毁,并不会随着局部作用域结束而结束生命。

        而语句      //TreeNode rootNode = TreeNode(rootVal);         //TreeNode* root = &rootNode; //& 给指针赋值,取地址   会报段错误  具体原因未知

「3」line65/67判断子树是否非空:

  -- 节省不必要的操作

       -- 函数 reConstructSubTree不能处理空vector的pre与vin,在该处判断非空可以保证递归调用时,不会出现空的vector的pre与vin。

「4」特殊输入的判断or异常处理:

       -- line16 两个vector向量不相等时,说明输入有误

       -- 保证了pre与vin输入非空,因此line23-24可以直接读取首元素并创建空节点。

       -- 当只有一个根节点时,还要判断pre与vin的这一个元素是相等的,否则输入有误。推出程序exit(1)。

       -- 在中序序列中寻找root位置时,要判断是否找到。如果没有找到,说明输入有误

       上述输入有误,针对测试实例3中输入的前序遍历序列与中序遍历序列不匹配的情况。

基础知识:

原文地址:https://www.cnblogs.com/GuoXinxin/p/9973793.html