题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出二叉树并输出它的头结点。二叉树结点的定义为:
struct BinaryTreeNode
{
int m_nValue ;
BinaryTreeNode* m_pLeft ;
BinaryTreeNode* m_pRight ;
} ;
一开始看到这道题的时候还真没有想法,后来也的确没有什么想法,基础太弱了,果断去看答案了。
先写一下书上面的思路:
在二叉树的前序遍历序列中,第一个数字总是树的根结点的值。但在中序遍历序列中,根结点的值在序列的中间,左子树的结点的值位于根结点的值的左边,而右子树的结点的值位于根结点的值的右边。因此我们需要扫描中序遍历序列,才能找到根结点的值。如题目给出的序列,前序遍历中的第一个数字1就是根结点的值。扫描中序遍历序列,就能确定根结点的值的位置。根据中序遍历的特点,在根结点的值1前面的3个数字都是左子树结点的值,位于1后面的数字都是右子树结点的值。这样我们就在前序遍历和中序遍历两个序列中,分别找到了左右子树对应的子序列。因此,可以用同样的方法去构建左右子树。也就是说,接下来的事情可以用递归的方法去完成。
详见代码:
#include<cstdio>
#include<iostream>
struct BinaryTreeNode
{
int m_nValue ;
BinaryTreeNode* m_pLeft ;
BinaryTreeNode* m_pRight ;
} ;
BinaryTreeNode* Construct(int* preorder,int* inorder,int length) ;
BinaryTreeNode* ConstructCore(int* startPreorder,int *endPreorder, int* startInorder,int * endInorder) ;
void PreOrderPrintTree(const BinaryTreeNode *pNode) ;
int main(void)
{
int nPreOrder[] = {1,2,4,7,3,5,6,8} ;
int nInOrder[] = {4,7,2,1,5,3,8,6} ;
BinaryTreeNode* root = Construct(nPreOrder,nInOrder,sizeof(nPreOrder)/sizeof(int)) ;
PreOrderPrintTree(root) ;
printf("\n") ;
return 0 ;
}
//前序遍历函数,用来验证结果
void PreOrderPrintTree(const BinaryTreeNode *pNode)
{
if(pNode != NULL)
{
printf("%d ",pNode->m_nValue) ;
PreOrderPrintTree(pNode->m_pLeft) ;
PreOrderPrintTree(pNode->m_pRight) ;
}
}
//从前序遍历和中序遍历,重建二叉树
BinaryTreeNode* Construct(int* preorder,int* inorder,int length) //length为序列的长度
{
if(preorder == NULL || inorder == NULL || length <= 0)
{
return NULL ;
}
return ConstructCore(preorder,preorder + length - 1 ,inorder,inorder + length - 1) ;
}
//核心函数
BinaryTreeNode* ConstructCore(int* startPreorder,int* endPreorder, int* startInorder,int* endInorder)
{
//前序遍历序列的第一个数字是根结点的值
int rootValue = startPreorder[0] ;
BinaryTreeNode* root = new BinaryTreeNode() ;
//构造一个结点
root->m_nValue = rootValue ;
root->m_pLeft = root->m_pRight = NULL ;
if(startPreorder == endPreorder)
{
if(startInorder == endInorder
&& *startPreorder == *startInorder) //只有一个结点
{
return root ;
}
else
throw std::exception("Invalid input.") ;
}
//在中序遍历中找到根结点的值
int* rootInorder = startInorder ;
while(rootInorder <= endInorder && *rootInorder != rootValue)
{
++rootInorder ;
}
//如果根结点等于中序遍历的末结点,则数据出错
if(rootInorder == endInorder && *rootInorder != rootValue)
{
throw std::exception("Invalid input.") ;
}
int leftLength = rootInorder - startInorder ; //中序遍历中根结点左结点数
int* leftPreorderEnd = startPreorder + leftLength ; //前序遍历中左子树结点的下一个位置
if(leftLength > 0)
{
//构建左子树,startPreorder+1为左子树的第一个结点,rootInorder-1为左子树的最后一个结点
root->m_pLeft = ConstructCore(startPreorder + 1,leftPreorderEnd,startInorder,rootInorder - 1) ;
}
if(leftLength < endPreorder - startPreorder)
{
//构建右子树
root->m_pRight = ConstructCore(leftPreorderEnd + 1 ,endPreorder,rootInorder + 1,endInorder) ;
}
return root ;
}
然后,自己模拟了一下有后序遍历序列和踱遍历序列的情况,如何去构建二叉树。
#include<iostream>
#include<cstdio>
struct BinaryTreeNode
{
int m_nValue ;
BinaryTreeNode* m_pLeft ;
BinaryTreeNode* m_pRight ;
} ;
BinaryTreeNode* Construct(int *postorder,int* inorder,int length) ;
BinaryTreeNode* ConstructCore(int *startPostorder,int *endPostorder,int* startInorder,int *endInorder) ;
void PrintPreOrder(BinaryTreeNode *pNode) ;
int main(void)
{
int nPostOrder[] = {7,4,2,5,8,6,3,1} ;
int nInOrder[] = {4,7,2,1,5,3,8,6} ;
BinaryTreeNode* root = Construct(nPostOrder,nInOrder,sizeof(nPostOrder)/sizeof(int)) ;
PrintPreOrder(root) ;
printf("\n") ;
return 0 ;
}
BinaryTreeNode* Construct(int *postorder,int* inorder,int length)
{
if(NULL == postorder || NULL == inorder || length <= 0)
{
return NULL ;
}
return ConstructCore(postorder,postorder + length -1 ,inorder,inorder + length - 1) ;
}
void PrintPreOrder(BinaryTreeNode *pNode)
{
if(pNode != NULL)
{
PrintPreOrder(pNode->m_pLeft) ;
PrintPreOrder(pNode->m_pRight) ;
printf("%d ",pNode->m_nValue) ;
}
}
BinaryTreeNode* ConstructCore(int *startPostorder,int *endPostorder,int* startInorder,int *endInorder)
{
int nValue = *endPostorder ;
BinaryTreeNode* root = new BinaryTreeNode ;
root->m_nValue = nValue ;
root->m_pLeft = root->m_pRight = NULL ;
if(startPostorder == endPostorder) //叶子结点
{
if(startInorder == endInorder && //两个序列都要判断,否则就是无效僌
*startPostorder == *endPostorder)
{
return root ;
}
else
{
throw std::exception("Invalid input.") ;
}
}
int* rootInorder = startInorder ;
while(rootInorder <= endInorder && *rootInorder != nValue)
{
rootInorder++ ;
}
if(*rootInorder != nValue && rootInorder == endInorder)
{
throw std::exception("Invalid input.") ;
}
int nRightLength = endInorder - rootInorder ;
int* rightPostorderStart = endPostorder - nRightLength ;
if(nRightLength > 0)
{
//构建右子树
root->m_pRight = ConstructCore(rightPostorderStart,endPostorder - 1,rootInorder + 1,endInorder) ;
}
if(startInorder < rootInorder)
{
//构建左子树
root->m_pLeft = ConstructCore(startPostorder,rightPostorderStart-1,startInorder,rootInorder - 1) ;
}
return root ;
}