1. 程式人生 > >已知二叉樹的中序遍歷和前序遍歷,如何求後序遍歷

已知二叉樹的中序遍歷和前序遍歷,如何求後序遍歷

昨天ACM集訓的時候出現了這道題,沒接觸過半天都沒做出來,但看到解法還是挺好理解的。

一道HULU的筆試題(How I wish yesterday once more)

假設有棵樹,長下面這個樣子,它的前序遍歷,中序遍歷,後續遍歷都很容易知道。


PreOrder:         GDAFEMHZ

InOrder:            ADEFGHMZ

PostOrder:       AEFDHZMG

現在,假設僅僅知道前序和中序遍歷,如何求後序遍歷呢?比如,已知一棵樹的前序遍歷是”GDAFEMHZ”,而中序遍歷是”ADEFGHMZ”應該如何求後續遍歷?

第一步,root最簡單,前序遍歷的第一節點G就是root。

第二步,繼續觀察前序遍歷GDAFEMHZ,除了知道G是root,剩下的節點必然是root的左右子樹之外,沒法找到更多資訊了。

第三步,那就觀察中序遍歷ADEFGHMZ。其中root節點G左側的ADEF必然是root的左子樹,G右側的HMZ必然是root的右子樹。

第四步,觀察左子樹ADEF,左子樹的中的根節點必然是大樹的root的leftchild。在前序遍歷中,大樹的root的leftchild位於root之後,所以左子樹的根節點為D。

第五步,同樣的道理,root的右子樹節點HMZ中的根節點也可以通過前序遍歷求得。在前序遍歷中,一定是先把root和root的所有左子樹節點遍歷完之後才會遍歷右子樹,並且遍歷的右子樹的第一個節點就是右子樹的根節點。

如何知道哪裡是前序遍歷中的左子樹和右子樹的分界線呢?通過中序遍歷去數節點的個數。

在上一次中序遍歷中,root左側是A、D、E、F,所以有4個節點位於root左側。那麼在前序遍歷中,必然是第1個是G,第2到第5個由A、D、E、F過程,第6個就是root的右子樹的根節點了,是M。

第六步,觀察發現,上面的過程是遞迴的。先找到當前樹的根節點,然後劃分為左子樹,右子樹,然後進入左子樹重複上面的過程,然後進入右子樹重複上面的過程。最後就可以還原一棵樹了。

第七步,其實,如果僅僅要求寫後續遍歷,甚至不要專門佔用空間儲存還原後的樹。只需要稍微改動第六步,就能實現要求。僅需要把第六步的遞迴的過程改動為如下:

1 確定根,確定左子樹,確定右子樹。

2 在左子樹中遞迴。

3 在右子樹中遞迴。

4 列印當前根。

參考了一些網上的討論,具體程式是:

  1. #include <iostream>
  2. #include <fstream>
  3. #include <string>
  4. struct TreeNode  
  5. {  
  6.   struct TreeNode* left;  
  7.   struct TreeNode* right;  
  8.   char  elem;  
  9. };  
  10. TreeNode* BinaryTreeFromOrderings(char* inorder, char* preorder, int length)  
  11. {  
  12.   if(length == 0)  
  13.     {  
  14.       return NULL;  
  15.     }  
  16.   TreeNode* node = new TreeNode;//Noice that [new] should be written out.
  17.   node->elem = *preorder;  
  18.   int rootIndex = 0;  
  19.   for(;rootIndex < length; rootIndex++)//a variation of the loop
  20.     {  
  21.       if(inorder[rootIndex] == *preorder)  
  22.       break;  
  23.     }  
  24.   node->left = BinaryTreeFromOrderings(inorder, preorder +1, rootIndex);  
  25.   node->right = BinaryTreeFromOrderings(inorder + rootIndex + 1, preorder + rootIndex + 1, length - (rootIndex + 1));  
  26.   std::cout<<node->elem<<std::endl;  
  27.   return node;  
  28. }  
  29. int main(int argc, char** argv){  
  30.     char* pr="GDAFEMHZ";      
  31.  char* in="ADEFGHMZ"; BinaryTreeFromOrderings(in, pr, 8); printf("\n"); return 0;}  


其實上面的程式碼寫得不夠簡潔。題目只要求輸出後續遍歷,並沒有要求建樹。所以,不需要去計算出node->left與node->right,也不需要去return node。改進版本如下

  1. struct TreeNode  
  2. {  
  3.   struct TreeNode* left;  
  4.   struct TreeNode* right;  
  5.   char  elem;  
  6. };  
  7. void BinaryTreeFromOrderings(char* inorder, char* preorder, int length)  
  8. {  
  9.   if(length == 0)  
  10.     {  
  11.       //cout<<"invalid length";
  12.       return;  
  13.     }  
  14.   TreeNode* node = new TreeNode;//Noice that [new] should be written out.
  15.   node->elem = *preorder;  
  16.   int rootIndex = 0;  
  17.   for(;rootIndex < length; rootIndex++)  
  18.     {  
  19.       if(inorder[rootIndex] == *preorder)  
  20.       break;  
  21.     }  
  22.   //Left
  23.   BinaryTreeFromOrderings(inorder, preorder +1, rootIndex);  
  24.   //Right
  25.   BinaryTreeFromOrderings(inorder + rootIndex + 1, preorder + rootIndex + 1, length - (rootIndex + 1));  
  26.   cout<<node->elem<<endl;  
  27.   delete node;   
  28.   return;  
  29. }  
  30. int main(int argc, char* argv[])  
  31. {  
  32.     printf("Hello World!\n");  
  33.     char* pr="GDAFEMHZ";  
  34.     char* in="ADEFGHMZ";  
  35.     BinaryTreeFromOrderings(in, pr, 8);  
  36.     printf("\n");  
  37.     return 0;  
  38. }  

再考慮一下,題目只要求輸出後續遍歷,所以其實連建立node也是沒有必要的。可以直接把當前節點的value儲存在一個char中,然後輸出。

  1. #include <stdio.h>
  2. #include <stdio.h>
  3. #include <iostream>
  4. usingnamespace std;  
  5. struct TreeNode  
  6. {  
  7.   struct TreeNode* left;  
  8.   struct TreeNode* right;  
  9.   char  elem;  
  10. };  
  11. void BinaryTreeFromOrderings(char* inorder, char* preorder, int length)  
  12. {  
  13.   if(length == 0)  
  14.     {  
  15.       //cout<<"invalid length";
  16.       return;  
  17.     }  
  18.   char node_value = *preorder;  
  19.   int rootIndex = 0;  
  20.   for(;rootIndex < length; rootIndex++)  
  21.     {  
  22.       if(inorder[rootIndex] == *preorder)  
  23.       break;  
  24.     }  
  25.   //Left
  26.   BinaryTreeFromOrderings(inorder, preorder +1, rootIndex);  
  27.   //Right
  28.   BinaryTreeFromOrderings(inorder + rootIndex + 1, preorder + rootIndex + 1, length - (rootIndex + 1));  
  29.   cout<<node_value<<endl;  
  30.   return;  
  31. }  
  32. int main(int argc, char* argv[])  
  33. {  
  34.     printf("Hello World!\n");  
  35.     char* pr="GDAFEMHZ";  
  36.     char* in="ADEFGHMZ";  
  37.     BinaryTreeFromOrderings(in, pr, 8);  
  38.     printf("\n");  
  39.     return 0;  
  40. }