题目描述:输入二叉树的前序遍历和中序遍历结果 ,请重建出该二叉树,返回其头节点并给出后序遍历序列。假设输入的遍历序列不包含重复数字。
题目分析:
根据前序遍历和中序遍历的特征,我们知道在前序遍历序列中,第一个数字总是树的根节点;而在中序遍历中根节点的值在序列中间,左子树的节点值一定位于根节点左边,右子树的节点值一定位于根节点右边。这样我们结合前序遍历和中序遍历序列,就可以确定出树的根节点及其左右子树节点,对于左右子树,递归地采用这种方法即可。
拿一个实例来说明上述算法描述:假设输入前序遍历序列{1,2,4,7,3,5,6,8}及中序遍历序列{4,7,2,1,5,3,8,6}。我们根据前序遍历序列知道:1是树的根节点,再结合中序遍历序列,知道{4,7,2}为根节点1的左子树,{5,3,8,6}为根节点1的右子树。对于两棵左右子树,重复上述算法即可重建整棵二叉树。
我们重用前面的二叉树链表实现代码给出重构二叉树的实现:
Node* BinaryTree::Reconstruct(int *preorder, int *inorder, int length)
{
if (preorder == NULL || inorder == NULL || length <= 0)
return NULL;
return ReconstructCore(preorder, preorder + length - 1, inorder, inorder + length - 1);
}
Node* BinaryTree::ReconstructCore(int *startPreorder, int *endPreorder, int *startInorder, int *endInorder)
{
//前序遍历第一个数字是根节点
int rootValue = startPreorder[0];
Node* root = new Node();
root->data = rootValue;
if (startPreorder == endPreorder)
{
if (startInorder == endInorder&&*startPreorder == *startInorder)
return root;
else
throw exception("Invalid input!");
}
//中序遍历中找到根节点
int *rootInorder = startInorder;
while (rootInorder <= endInorder&&*rootInorder != rootValue)
++rootInorder;
if (rootInorder == endInorder&&*rootInorder != rootValue)
throw exception("Invalid input!");
//重建左右子树
int leftLength = rootInorder - startInorder;
int *leftPreorderEnd = startPreorder + leftLength;
if (leftLength > 0) //左子树不为空
{
root->p_lChild = ReconstructCore(startPreorder + 1, leftPreorderEnd, startInorder, rootInorder - 1);
root->p_lChild->p_Parent = root;
}
if (leftLength < endPreorder - startPreorder) //重建右子树
{
root->p_rChild = ReconstructCore(leftPreorderEnd + 1, endPreorder, rootInorder + 1, endInorder);
root->p_rChild->p_Parent = root;
}
return root;
}
几点说明:
- 上述代码中包含了大量的指针加减运算,读者应注意理解,实际上是运算结果指针的移动。
- 重建二叉树的过程中并没有指定索引,建议重构完成之后再指定索引值。
- 后序遍历可以重用前面已经实现的代码。
- 同样地,当给出后序遍历和中序遍历序列后,也可以用类似的方法重构出二叉树。但是给出前序遍历和后序遍历则不能完成重构(有兴趣的读者可以想想为什么)。