已知二叉樹的中序遍歷和前序遍歷,如何求後序遍歷
昨天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 列印當前根。
參考了一些網上的討論,具體程式是:
- #include <iostream>
- #include <fstream>
- #include <string>
- struct TreeNode
- {
- struct TreeNode* left;
- struct TreeNode* right;
- char elem;
- };
- TreeNode* BinaryTreeFromOrderings(char* inorder, char* preorder, int length)
- {
- if(length == 0)
- {
- return NULL;
- }
- TreeNode* node = new TreeNode;//Noice that [new] should be written out.
- node->elem = *preorder;
- int rootIndex = 0;
- for(;rootIndex < length; rootIndex++)//a variation of the loop
- {
- if(inorder[rootIndex] == *preorder)
- break;
- }
- node->left = BinaryTreeFromOrderings(inorder, preorder +1, rootIndex);
- node->right = BinaryTreeFromOrderings(inorder + rootIndex + 1, preorder + rootIndex + 1, length - (rootIndex + 1));
- std::cout<<node->elem<<std::endl;
- return node;
- }
- int main(int argc, char** argv){
- char* pr="GDAFEMHZ";
- char* in="ADEFGHMZ"; BinaryTreeFromOrderings(in, pr, 8); printf("\n"); return 0;}
其實上面的程式碼寫得不夠簡潔。題目只要求輸出後續遍歷,並沒有要求建樹。所以,不需要去計算出node->left與node->right,也不需要去return node。改進版本如下
- struct TreeNode
- {
- struct TreeNode* left;
- struct TreeNode* right;
- char elem;
- };
- void BinaryTreeFromOrderings(char* inorder, char* preorder, int length)
- {
- if(length == 0)
- {
- //cout<<"invalid length";
- return;
- }
- TreeNode* node = new TreeNode;//Noice that [new] should be written out.
- node->elem = *preorder;
- int rootIndex = 0;
- for(;rootIndex < length; rootIndex++)
- {
- if(inorder[rootIndex] == *preorder)
- break;
- }
- //Left
- BinaryTreeFromOrderings(inorder, preorder +1, rootIndex);
- //Right
- BinaryTreeFromOrderings(inorder + rootIndex + 1, preorder + rootIndex + 1, length - (rootIndex + 1));
- cout<<node->elem<<endl;
- delete node;
- return;
- }
- int main(int argc, char* argv[])
- {
- printf("Hello World!\n");
- char* pr="GDAFEMHZ";
- char* in="ADEFGHMZ";
- BinaryTreeFromOrderings(in, pr, 8);
- printf("\n");
- return 0;
- }
再考慮一下,題目只要求輸出後續遍歷,所以其實連建立node也是沒有必要的。可以直接把當前節點的value儲存在一個char中,然後輸出。
- #include <stdio.h>
- #include <stdio.h>
- #include <iostream>
- usingnamespace std;
- struct TreeNode
- {
- struct TreeNode* left;
- struct TreeNode* right;
- char elem;
- };
- void BinaryTreeFromOrderings(char* inorder, char* preorder, int length)
- {
- if(length == 0)
- {
- //cout<<"invalid length";
- return;
- }
- char node_value = *preorder;
- int rootIndex = 0;
- for(;rootIndex < length; rootIndex++)
- {
- if(inorder[rootIndex] == *preorder)
- break;
- }
- //Left
- BinaryTreeFromOrderings(inorder, preorder +1, rootIndex);
- //Right
- BinaryTreeFromOrderings(inorder + rootIndex + 1, preorder + rootIndex + 1, length - (rootIndex + 1));
- cout<<node_value<<endl;
- return;
- }
- int main(int argc, char* argv[])
- {
- printf("Hello World!\n");
- char* pr="GDAFEMHZ";
- char* in="ADEFGHMZ";
- BinaryTreeFromOrderings(in, pr, 8);
- printf("\n");
- return 0;
- }