Given preorder and inorder traversal of a tree, construct the binary tree.
Note:
You may assume that duplicates do not exist in the tree.
题目如上所示:给出二叉树的先序遍历序列和中序遍历序列,复原二叉树。
分析:
1)题目拿过来,首先要清楚二叉树的三种遍历方式:
前序遍历(Preorder),首先访问根节点,然后遍历左子树,最后遍历右子树(根-左-右)
中序遍历(inorder),首先遍历左子树,然后访问根节点,最后遍历右子树(左-根-右)
后序遍历(postorder),首先遍历左子树,然后遍历右子树,最后访问根节点(左-右-根)
可见,二叉树的三种遍历方式是从根节点角度来看的,其遍历方法可以查看算法导论,如果没记错的话是采用涂色(黑、白、灰)的方法实现遍历的。
现举一例:
上图中的二叉树遍历:
先序:ABDCEFGH
中序:BDAFEHGC
后序:DBFHGECA
2)现在回归到问题本身,给出先序序列和中序序列,要复原二叉树,为了分析方便我们仍以上图中树形为例:
12345678
先序 ABDCEFGH
中序 BDAFEHGC
根据先序序列,可以知道A为整棵树的根(先序序列第一个节点为整棵树的根),由此可以通过对中序序列进行扫描得到:
左子树的中序:BD
右子树的中序:FEHGC
另外一点我们知道,先序序列与中序序列的长度肯定是相同的,那么由此,我们又可以得到
左子树的先序:BD
右子树的先序:CEFGH
这样我们将一个大的问题划分为两个小问题,即要想复原一棵树,只需复原其根的左子树和右子树即可,由此可以采用递归的方法(其实树的问题大多都可以使用递归)
3)确定的递归方法之后,要找出递归出口,经过分析,这个问题的递归出口很简单:
a.先序(中序)序列中只有一个元素时,那么树种只有一个节点,直接返回
b.先序(中序)序列中没有元素的,空树,返回NULL。
由此得出以下代码:
// 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 *buildTree(vector<int> &preorder, vector<int> &inorder) {
vector<int>::iterator iter;
//递归出口
if (preorder.empty())
{
return NULL;
}
if (preorder.size()==1)
{
TreeNode * root=new TreeNode(preorder[0]);
return root;
}
//根据先序序列构造整棵树的根节点
TreeNode * root=new TreeNode(preorder[0]);
//获取根节点在中序序列中的位置,同时分解中序序列为“左子树”和“右子树”
int pos=0;
vector <int> leftInorder;
vector <int> rightInorder;
for (iter=inorder.begin();iter<inorder.end();iter++)
{
if (*iter==root->val)
{
break;
}
leftInorder.push_back(*iter);
pos++;
}
pos++;
for (iter=inorder.begin()+pos;iter<inorder.end();iter++)
{
rightInorder.push_back(*iter);
}
//递归求解
vector <int> leftPreorder;
vector <int> rightPreorder;
for (iter=preorder.begin()+1;iter<preorder.begin()+pos;iter++)
{
leftPreorder.push_back(*iter);
}
for (;iter<preorder.end();iter++)
{
rightPreorder.push_back(*iter);
}
root->left=buildTree(leftPreorder,leftInorder);
root->right=buildTree(rightPreorder,rightInorder);
return root;
}
};
提交,发现Memory Limited Exceeded!原因就在于递归中申请了太多空间,其实经过仔细分析,题目中给出的函数入口格式并不好,因为我们没有必要为先序和中序序列申请新的空间(算法中并不改变序列本身),为此自定义一个函数格式,代码如下
// 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 *buildTree(vector<int> &preorder, vector<int> &inorder) {
//vector<int>::iterator iter;
递归出口
//if (preorder.empty())
//{
// return NULL;
//}
//if (preorder.size()==1)
//{
// TreeNode * root=new TreeNode(preorder[0]);
// return root;
//}
根据先序序列构造整棵树的根节点
// TreeNode * root=new TreeNode(preorder[0]);
获取根节点在中序序列中的位置,同时分解中序序列为“左子树”和“右子树”
//int pos=0;
//vector <int> leftInorder;
//vector <int> rightInorder;
//for (iter=inorder.begin();iter<inorder.end();iter++)
//{
// if (*iter==root->val)
// {
// break;
// }
// leftInorder.push_back(*iter);
// pos++;
//}
//pos++;
//for (iter=inorder.begin()+pos;iter<inorder.end();iter++)
//{
// rightInorder.push_back(*iter);
//}
//
递归求解
//vector <int> leftPreorder;
//vector <int> rightPreorder;
//for (iter=preorder.begin()+1;iter<preorder.begin()+pos;iter++)
//{
// leftPreorder.push_back(*iter);
//}
//for (;iter<preorder.end();iter++)
//{
// rightPreorder.push_back(*iter);
//}
//root->left=buildTree(leftPreorder,leftInorder);
//root->right=buildTree(rightPreorder,rightInorder);
//return root;
return build(preorder,inorder,0,0,preorder.size());
}
TreeNode* build(vector<int> &preorder, vector<int> &inorder,int prestart,
int instart,int seqLen)
{
vector<int>::iterator iter;
/* cout<<"========================="<<endl;
cout<<"Prestart:"<<prestart<<endl;
cout<<"InStart:"<<instart<<endl;
cout<<"seqLen"<<seqLen<<endl;
for (iter=preorder.begin()+prestart;iter<preorder.begin()+prestart+seqLen;iter++)
{
cout<<*iter<<" ";
}
cout<<endl;*/
/* for (iter=inorder.begin()+instart;iter<inorder.begin()+instart+seqLen;iter++)
{
cout<<*iter<<" ";
}
cout<<endl;*/
//递归出口
if (seqLen==0)
{
return NULL;
}
if (seqLen==1)
{
TreeNode * root=new TreeNode(preorder[prestart]);
return root;
}
//根据先序序列构造整棵树的根节点
TreeNode * root=new TreeNode(preorder[prestart]);
//获取根节点在中序序列中的位置,同时分解中序序列为“左子树”和“右子树”的起点和长度
int leftLen=0;//左子树长度
int rightLen=0;//右子树长度
for (iter=(inorder.begin()+instart);iter<(inorder.begin()+instart+seqLen);iter++)
{
if (*iter==root->val)
{
break;
}
leftLen++;
}
/*leftLen++;*/
for (iter=(inorder.begin()+instart+leftLen+1);iter<(inorder.begin()+instart+seqLen);iter++)
{
rightLen++;
}
root->left=build(preorder,inorder,prestart+1,instart,leftLen);
root->right=build(preorder,inorder,(prestart+leftLen+1),(instart+leftLen+1),rightLen);
return root;
}
};