題目:輸入某二叉樹的前序遍歷和中序遍歷結果,請重建出該二叉樹。假設ios
輸入的前序遍歷和中序遍歷的結果都不含重複的數字。例如輸入前序遍歷ide
序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建出如圖spa
所示的二叉樹並輸出它的頭結點。二叉樹的定義以下:3d
1 struct BinaryTreeNode 2 { 3 int m_nValue; 4 BinaryTreeNode* m_pLeft; 5 BinaryTreeNode* m_pRight; 6 }
1 1 2 / \ 3 2 3 4 / / \ 5 4 5 6 6 \ / 7 7 8
首先要明白一些基本概念:前序遍歷,中序遍歷,後續遍歷code
以上圖中的二叉樹爲例:blog
前序遍歷:個人理解是 「根-左-右」 先訪問根結點 再訪問左子樹 最好訪問右子樹遞歸
好比:先訪問根結點1,再訪問左子節點,左子節點爲一顆子樹,那麼又用根-左-右io
的規則,先訪問根節點2,再訪問左在節點4,再訪問右子節點。這樣知道全部event
節點遍歷結束:那麼遍歷的節點列表是:1,2,4,7,3,5,6,8class
中序遍歷遵循規則:「左-根-右」 遍歷的節點列表是:4,7,2,1,5,3,8,6
後序遍歷遵循規則:「左-右-根」 遍歷的節點列表是:7,4,2,5,8,6,3,1
那麼從另一個方面來講:任何一個二叉樹三種遍歷方式的結果是惟一。
反之也就是說這三種遍歷結果惟一的肯定一顆二叉樹。
其實當咱們分析會發現實際上在惟一肯定一個二叉樹的時候並不是
徹底給出三種方式的遍歷結果,實際上咱們只須要前序遍歷與
中序遍歷 又或者中序遍歷與後序遍歷。注意,若是僅僅是先序
遍歷和後序遍歷是不能惟一肯定一個二叉樹的。這點是須要特別注意的
下面咱們來分析一下:
若是咱們只知道一個二叉樹的中序遍歷:4,7,2,1,5,3,8,6
與後序遍歷:7,4,2,5,8,6,3,1
如今咱們開分析一下能不能經過這兩中遍歷結果推出先序
遍歷結果:1,2,4,7,3,5,6,8
咱們知道後序遍歷可知,遍歷列表最後一個元素爲二叉樹的根結點
可知該最後一個元素爲1,那麼跟結點爲1.
在中序遍歷結果中找到根節點所在的位置,那麼該位置前面的即爲
二叉樹的左子樹:4,7,2 該位置後面的爲二叉樹的右子樹:5,3,8,6
那麼咱們能夠經過找到的左右子樹肯定後序遍歷的左右子樹遍歷結果:
7,4,2和5,8,6,3
如今咱們又分別針對每一個子樹重複上述步驟:顯然前序遍歷結果爲:1,2,4,7,3,5,8,6
很顯然前序與中序結果退出後序遍歷結果的步驟是同樣同樣的。
那麼問題來了,爲何不能通道前序和後序遍歷結果推出中序遍歷結果呢?
由於咱們不能經過前序和中序遍歷肯定遍歷結果中關於根節點的左右子樹。
也就是說前序和後序遍歷結果重建的二叉樹不必定是惟一的。
好了有了前面的分析咱們不難經過遞歸的方式解決這道題,
劍指Offer書上是經過前序和中序遍歷推出後序遍歷()
代碼以下:
1 #include <iostream> 2 using namespace std; 3 4 struct BinaryTreeNode 5 { 6 int m_nValue; 7 BinaryTreeNode* m_pLeft; 8 BinaryTreeNode* m_pRight; 9 }; 10 11 12 BinaryTreeNode* ConstructCore(int* startPreorder,int* endPreorder,int*startInorder,int* endInorder) 13 { 14 int rootValue = startPreorder[0]; 15 struct BinaryTreeNode* root = (struct BinaryTreeNode*)malloc(sizeof(struct BinaryTreeNode)); 16 root->m_nValue=rootValue; 17 root->m_pLeft=NULL; 18 root->m_pRight=NULL; 19 20 if(startPreorder == endPreorder) 21 { 22 if(startInorder == endInorder&& *startPreorder==*startInorder) 23 return root; 24 else 25 throw exception("Invalid Input."); 26 } 27 28 int* rootInorder=startInorder; 29 while(rootInorder<=endInorder&&*rootInorder!=rootValue) 30 ++rootInorder; 31 32 if(rootInorder==endInorder&&*rootInorder!=rootValue) 33 throw exception("Invalid Input."); 34 35 int leftLength = rootInorder-startInorder; 36 int* LeftPreorderEnd=startPreorder+leftLength; 37 if(leftLength>0) 38 { 39 root->m_pLeft=ConstructCore(startPreorder+1,LeftPreorderEnd,startInorder,rootInorder-1); 40 } 41 42 if(leftLength<endPreorder-startPreorder) 43 { 44 root->m_pRight=ConstructCore(LeftPreorderEnd+1,endPreorder,rootInorder+1,endInorder); 45 } 46 47 48 return root; 49 } 50 51 52 BinaryTreeNode* Construct(int* preorder,int* inorder,int length) 53 { 54 if(preorder==NULL||inorder==NULL||length<=0) 55 return NULL; 56 return ConstructCore(preorder,preorder+length-1,inorder,inorder+length-1); 57 } 58 59 60 void AfterOrderPrint(struct BinaryTreeNode* root) 61 { 62 if(root) 63 { 64 AfterOrderPrint(root->m_pLeft); 65 AfterOrderPrint(root->m_pRight); 66 cout<<root->m_nValue<<" "; 67 } 68 } 69 70 71 int main() 72 { 73 BinaryTreeNode *Root; 74 int preList[8]={1,2,4,7,3,5,6,8}; 75 int inList[8]={4,7,2,1,5,3,8,6}; 76 Root=Construct(preList,inList,8); 77 AfterOrderPrint(Root); 78 cout<<endl; 79 return 0; 80 }
截圖:
對的吧
接下來咱們模仿劍指offer書中的上述代碼,經過先後序遍歷和中序遍歷
推出前序遍歷進而重建這顆二叉樹。
1 #include <iostream> 2 using namespace std; 3 4 struct BinaryTreeNode 5 { 6 int m_nValue; 7 BinaryTreeNode* m_pLeft; 8 BinaryTreeNode* m_pRight; 9 }; 10 11 12 BinaryTreeNode* ConstructCore(int* startAfterorder,int* endAfterorder,int*startInorder,int* endInorder) 13 { 14 int rootValue = endAfterorder[0]; 15 struct BinaryTreeNode* root = (struct BinaryTreeNode*)malloc(sizeof(struct BinaryTreeNode)); 16 root->m_nValue=rootValue; 17 root->m_pLeft=NULL; 18 root->m_pRight=NULL; 19 20 int* rootInorder=startInorder; 21 while(rootInorder<=endInorder&&*rootInorder!=rootValue) 22 ++rootInorder; 23 24 int leftLength = rootInorder-startInorder; 25 int* LeftAfterorderEnd=startAfterorder+leftLength; 26 if(leftLength>0) 27 { 28 root->m_pLeft=ConstructCore(startAfterorder,LeftAfterorderEnd-1,startInorder,rootInorder-1); 29 } 30 31 if(leftLength<endAfterorder-startAfterorder) 32 { 33 root->m_pRight=ConstructCore(LeftAfterorderEnd,endAfterorder-1,rootInorder+1,endInorder); 34 } 35 36 37 return root; 38 } 39 40 41 BinaryTreeNode* Construct(int* Afterorder,int* inorder,int length) 42 { 43 if(Afterorder==NULL||inorder==NULL||length<=0) 44 return NULL; 45 return ConstructCore(Afterorder,Afterorder+length-1,inorder,inorder+length-1); 46 } 47 48 49 void PreOrderPrint(struct BinaryTreeNode* root) 50 { 51 if(root) 52 { 53 cout<<root->m_nValue<<" "; 54 PreOrderPrint(root->m_pLeft); 55 PreOrderPrint(root->m_pRight); 56 } 57 } 58 59 60 int main() 61 { 62 BinaryTreeNode *Root; 63 int AfterList[8]={7,4,2,5,8,6,3,1}; 64 int inList[8]={4,7,2,1,5,3,8,6}; 65 Root=Construct(AfterList,inList,8); 66 PreOrderPrint(Root); 67 cout<<endl; 68 return 0; 69 }
咦 正確了哦
兩點說明:
1.使用前序中序以及中序後序重建一個二叉樹,這顆二叉樹中的節點元素值必須徹底不相等。
爲何呢,假設除開根節點外還有另一個節點值爲1 那麼在前序或者後序遍歷中怎麼能
肯定這個節點左子樹和右子樹的分界點呢,既然不能肯定,那麼顯然不能遞歸重建一個惟一的
二叉樹
2.注意關於前序中序重建二叉樹時應該注意前序遍歷與中序遍歷的一致性,不然沒法重建這可二叉樹。