问题描述
设有一颗二叉树,每个节点的数据都不相同,通过这个二叉树的前序序列和中序序列来构建这颗二叉树。
思路
用上面的两个序列举例,首先把目光聚焦在前序序列上。能够确定的是:前序序列的第一个元素是这颗二叉树的根节点。所以preOrder[0]就是根节点,其值为:0。有了根节点再结合中序序列我们就能知道左子树的范围以及右子树的范围。
如图所示,在中序序列(inOrder)中inOrder[3]为根节点,索引为[0,2]的元素构成了根节点的左子树,索引为[4,6]的元素构成了根节点的右子树。
在中序序列(inOrder)中root左边的元素数量有三个,也就是说前序序列(preOrder)中往后的三个元素都是root左边的元素。因为前序遍历会将根节点以及左子树的节点全部遍历完了才会遍历右子树的节点。
现在我们确认了左子树在前序序列(preOrder)和中序序列(inOrder)中的范围。就可以递归重复刚才的步骤了。
代码
template<typename E>
binaryTreeNode<E>* linkedBinaryTree<E>::buildTree(
const E* const preOrderList, const int thePreStart, const int thePreEnd,
const E* const inOrderList, const int theInStart, const int theInEnd)
{
/*
* preOrderList:前序数组 thePreStart:前序开始索引 thePreEnd:前序结束索引号
* inOrderList:中序数组 theInStart:中序开始索引 theInEnd:中序结束索引号
*/
if (thePreStart > thePreEnd) //递归退出条件
return nullptr;
//当前节点
binaryTreeNode<E>* theRoot = new binaryTreeNode<E>(preOrderList[thePreStart]);
//找到当前节点在中序序列中的索引号==>index
int index = theInStart;
while (inOrderList[index] != theRoot->element)
index++;
//求出左子树的节点数量==>leftSize
int leftSize = index - theInStart;
theRoot->left = buildTree(
preOrderList, thePreStart + 1, thePreStart + leftSize,
inOrderList, theInStart, index - 1);
theRoot->right = buildTree(
preOrderList, thePreStart + leftSize + 1, thePreEnd,
inOrderList, index + 1, theInEnd);
return theRoot;
}
这里注意一下 :我们是如何确定下一步的边界的?
theRoot->left = buildTree(
preOrderList, thePreStart + 1, thePreStart + leftSize,
inOrderList, theInStart, index - 1);
theRoot->right = buildTree(
preOrderList, thePreStart + leftSize + 1, thePreEnd,
inOrderList, index + 1, theInEnd);
- 对于左子树来
前序序列的边界:[当前的前序开始索引 + 1,当前的前序开始索引+左子树的节点数]
中序序列的边界:[当前的中序开始索引,当前元素在中序序列中的索引号 - 1]
- 对于右子树来说
前序序列的边界:[当前的前序的开始索引 + 左子树的节点数 + 1,当前前序的结束索引]
中序序列的边界:[当前元素在中序序列中的索引号 + 1,当前中序的结束索引]
如何确定何时退出递归呢
- 对于左子树来说
其前序序列的左边界是由当前的前序开始索引thePreStart + 1得来的,其前序序列的右边界是通过当前thePreStart + 左子树的节点数得来的。那么也就是说:如果左子树的节点数为0,也就是说当前节点的左子节点为空时,下一步的参数thePreStart就会大于参数thePreEnd。
- 对于右子树来说
左边的边界会随着递归一直往右走,递归到最后一个元素时下一步的左边界thePreStart会大于右边界thePreEnd。
结论:当thePreStart > thePreEnd时返回nullptr
改进代码
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.empty() || inorder.empty())
return nullptr;
if(preorder.size() != inorder.size())
return nullptr;
map<int,int> inverse;
for(int i = 0; i < preorder.size();i++)
inverse[inorder[i]] = i;
return build(preorder,0,preorder.size()-1,inorder,0,inverse);
}
TreeNode* build(vector<int>& preorder,const int thePreStart,const int thePreEnd,vector<int>& inorder,const int theInStart,map<int,int>& inverse){
if(thePreStart > thePreEnd)
return nullptr;
TreeNode* root = new TreeNode(preorder[thePreStart]);
int theIndex = inverse[root->val];
int leftSize = theIndex - theInStart;
root->left = build(preorder,thePreStart+ 1,thePreStart + leftSize,inorder,theInStart,inverse);
root->right = build(preorder,thePreStart + leftSize + 1,thePreEnd,inorder,theIndex + 1,inverse);
return root;
}