整理自剑指Offer和牛客网的讨论 https://www.nowcoder.com/questionTerminal/8b3b95850edb4115918ecebdf1b4d222
一:题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
二:解题思路
在二叉树的前序遍历中,第一个数总数根节点的值。但在中序遍历中,根节点的值在序列的中间,左子树的结点的值位于根节点的的值得左边,而右子树的结点的值位于根节点的右边,因此我们需要扫描中序遍历序列,才能找到根节点的值。
以例子说明:
如图所示,前序遍历第一个数字1就是根节点的值。扫描中序遍历序列,就能确定根节点值得位置。根据中序遍历的特点,在根节点的值1前面的3个数字都是左子树结点的值,位于1后面的数字都是右子树结点的值。
由于在中序遍历中有3个数字都是左子树结点的值,因此左子树总共有3个左子结点。同样在前序遍历的序列中,根节点后面的3个数字就是3个左子树结点的值。再后面的所有数字都是右子树结点的值。这样我们就在前序遍历和中序遍历中,分别找到了左右子树对应的子序列。
接下来就可以在左子树对应的先序与中序序列中递归调用函数找到当前节点的左结点,右子树对应的先序和中序序列中递归调用函数找到当前节点的有结点
总结方法的步骤(递归):
1.前序遍历序列第一个值是根节点,创建根节点
2.在中序遍历序列中找到根节点位置(根据中序遍历中根节点的位置,可以获得左右子树的大小)
3.计算左子树前序遍历序列的起始终止位置,中序遍历序列的起始终止位置
4.计算右子树前序遍历序列的启示终止位置,中序遍历序列的起始终止位置
例如
先序遍历序列pre
起始位置startPre
终止位置endPre
中序遍历序列vin
起始位置startVin
终止位置endVin
根节点位于中序遍历序列第i个位置,则左子树的长度为(i-startVin),右子树长度(endVin-i)
所以:
左子树:
先序遍历 起始:startPre+1 终止:startPre+i-startVin
中序遍历 起始:startVin 终止:i-1
右子树:
先序遍历 起始:startPre+i-startVin+1(左子树终止+1) 终止 endPre
中序遍历 起始:i+1 终止 endVin
三:代码实现
/**
* 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* reConstructBinaryTreeCore(vector<int> pre,int startPre,int endPre,vector<int> vin,int startVin,int endVin){
if(startPre>endPre || startVin>endVin)
return NULL;
//创建当前根结点
TreeNode* root=new TreeNode(pre[startPre]);
//在中序遍历中找到根结点
for(int i=startVin;i<=endVin;i++)
if(vin[i]==pre[startPre]){
//重新计算左子树pre-startPre,endPre vin--startVin.endVin
root->left=reConstructBinaryTreeCore(pre,startPre+1,startPre+i-startVin,vin,startVin,i-1);
//重新计算右子树pre-startPre,endPre vin--startVin.endVin
root->right=reConstructBinaryTreeCore(pre,i-startVin+startPre+1,endPre,vin,i+1,endVin);
//这种方法很巧妙,不用定义额外的变量存放左右子树中序,先序的信息
}
return root;
}
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
if(pre.size()==0 || vin.size()==0)
return NULL ;
TreeNode* root=reConstructBinaryTreeCore(pre,0,pre.size()-1,vin,0,vin.size()-1);
return root;
}
};