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 };
注意:「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中输入的前序遍历序列与中序遍历序列不匹配的情况。
基础知识: