《剑指Offer》读书笔记---面试题6:重建二叉树

来源:互联网 发布:疯狗雾化器做丝数据 编辑:程序博客网 时间:2024/05/02 03:55

题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出二叉树并输出它的头结点。二叉树结点的定义为:

struct BinaryTreeNode{intm_nValue ;BinaryTreeNode*m_pLeft ;BinaryTreeNode*m_pRight ;} ;


一开始看到这道题的时候还真没有想法,后来也的确没有什么想法,基础太弱了,果断去看答案了。


先写一下书上面的思路:

在二叉树的前序遍历序列中,第一个数字总是树的根结点的值。但在中序遍历序列中,根结点的值在序列的中间,左子树的结点的值位于根结点的值的左边,而右子树的结点的值位于根结点的值的右边。因此我们需要扫描中序遍历序列,才能找到根结点的值。如题目给出的序列,前序遍历中的第一个数字1就是根结点的值。扫描中序遍历序列,就能确定根结点的值的位置。根据中序遍历的特点,在根结点的值1前面的3个数字都是左子树结点的值,位于1后面的数字都是右子树结点的值。这样我们就在前序遍历和中序遍历两个序列中,分别找到了左右子树对应的子序列。因此,可以用同样的方法去构建左右子树。也就是说,接下来的事情可以用递归的方法去完成。

详见代码:

#include<cstdio>#include<iostream>struct BinaryTreeNode{intm_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){returnNULL ;}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 ;}