题目要求:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回【来源于剑指offer】。
基本思路:
1.先求出根节点(前序序列第一个元素)。
2.将根节点带入到中序遍历序列中求出左右子树的中序遍历序列。
3.通过左右子树的中序序列长度、位置关系
带入前序遍历序列,求出左右子树的前序序列。
4.左右子树的前序序列第一个元素分别是根节点的左右儿子
5.求出了左右子树的4种序列可以递归上述步骤。
代码实现
方法一新建四个序列,分别存储前序中序的左右子树。
注释内容:理解代码之后独立完成过程中,曾犯过的错误以及相应的思考,并非对程序思路的解读。
#include<iostream>
#include<vector>
struct TreeNode //此处无指针
{
int val;
TreeNode* Left; //指针符号紧跟哪个,有讲究吗???
TreeNode* Right;
//注意结构体以及初始化格式:逗号,{},无分号
TreeNode(int x) :val(x), Left(NULL), Right(NULL) {}
};
TreeNode* reConstructBinaryTree(std::vector<int> pre, std::vector<int> vin)
{
if (pre.size() == 0 || vin.size() == 0)
return NULL;
std::vector<int> pre_left, pre_right,vin_left,vin_right;
// 定义类型一定为指针型,到现在还不是特别理解,需要补一些基础知识啦~~
//初始化时要用new啊,不是直接TreeNode *tree(pre[0])!!!
//再次在new处踩坑,明明错误提示没有初始化就是不知道怎么改!!!
TreeNode* root=new TreeNode(pre[0]);
int i = 0;
for (i = 0; vin[i] != pre[0]; i++);
for (int j = 0; j < pre.size(); j++)
{
if (j < i)
{
//向量要压入压出,不是直接赋值
//pre_left[order] = pre[order+1];
pre_left.push_back(pre[j+1]);//注意要加一
vin_left.push_back(vin[j]);
}
if (j > i)
{
//注意序号,无需与i做加减运算
pre_right.push_back(pre[j]);
vin_right.push_back(vin[j]);
}
}
root->Left = reConstructBinaryTree(pre_left, vin_left);
root->Right = reConstructBinaryTree(pre_right, vin_right);
return root;
}
void main()
{
TreeNode* tree;
std::vector<int> pre = { 1,2,4,7,3,5,6,8 };
std::vector<int> vin = { 4,7,2,1,5,3,8,6 };
tree = reConstructBinaryTree(pre, vin);
return 1;
}
代码实现
方法二没有新建序列,而是在原序列中不断更新左右子树的边界值完成递归,因此,向量形参为pre vin永远未改变。
注释内容:理解代码之后独立完成过程中,曾犯过的错误以及相应的思考理解。
#include<iostream>
#include<vector>
struct TreeNode
{
int val;
TreeNode* Left;
TreeNode* Right;
TreeNode(int x):val(x),Left(NULL),Right(NULL){}
};
TreeNode* reConstructBinaryTree(std::vector<int> pre, int pre_left, int pre_right, std::vector<int> vin, int vin_left, int vin_right)
{
//由于pre vin序列未改变,只能通过边界判断子树是否为空;不能通过pre.size()判断,曾在此处踩坑,误以为等价
if (pre_left > pre_right || vin_left > vin_right)
return NULL;
TreeNode* tree = new TreeNode(pre[pre_left]);
//for (i = 0; pre[0] != in[i]; i++);
/*前序遍历边界确定前序遍历的新子树;中序遍历边界确定中序遍历的新子树,所以,i应该从in_first开始而不是pre_first;不可混淆,曾在此处踩坑,且每层递归的内容为左右边界确定的新子树,所以,函数中应全部为形参pre_first,不应出现 0 作为数组首位*/
//for (int i = 0; i < pre.size(); i++)//此处虽然正确,但增大了循环量//(20181221更新)此处不正确,若不限定范围,对于对称树,有重复值,会出现问题
for (i = in_first; i < in_last + 1; i++)
{
if (pre[pre_left] == vin[i])
{
/*计算递归边界实参:容易确定vin序列左右子树边界,通过pre、vin序列长度相等来确定pre序列左右子树的边界;不要轻易地
以为in的左子树的右边界为i;仅仅第一次时符合,递归至第二层时已经不符合了*/
tree->Left = reConstructBinaryTree(pre, pre_left + 1, i - vin_left + pre_left, vin, vin_left, i - 1);
//前序序列的左子树右边界+1即为右子树的左边界,或者根据长度相等来确定
tree->Right = reConstructBinaryTree(pre, i - vin_left + pre_left + 1, pre_right, vin, i + 1, vin_right);
}
}
return tree;
}
TreeNode* reConstructBinaryTree(std::vector<int> pre, std::vector<int> vin)
{
TreeNode* tree = reConstructBinaryTree(pre, 0, pre.size() - 1, vin, 0, pre.size() - 1);
return tree;
}
void main()
{
TreeNode* tree;
std::vector<int> pre = { 1,2,4,7,3,5,6,8 };
std::vector<int> vin = { 4,7,2,1,5,3,8,6 };
tree = reConstructBinaryTree(pre, vin);
return 1;
}
曾因未初始化提示错误查错时,看到如下内容,目前不太理解,不太会用Call Stack去调试程序。
按Alt+7键查看Call Stack即“调用堆栈”里面从上到下列出的对应从里层到外层的函数调用历史。双击某一行可将光标定位到此次调用的源代码或汇编指令处,看不懂时双击下一行,直到能看懂为止。
由于还没学习到时间复杂度、空间复杂度等相关内容,暂时对以上两种不同传参的方法还无法进行进一步分析。