題目:輸入某二叉樹的前序走訪和中序遍曆的結果,請重建出該二叉樹。假設輸入的前序走訪和中序遍曆的結果中都不含重複的數字。例如輸入前序走訪序列{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 ;}