二叉树后序遍历的四种方法
在二叉樹三種順序的遍歷中,后序遍歷相對較麻煩一些,其實對于遞歸方法來說,三種方法大同小異,思路與實現都很簡單。后序遍歷的迭代法與Morris方法比較麻煩。這里介紹后序遍歷的四種方法,其實還是遞歸、迭代和Morris方法,只不過在迭代中有幾種實現方式。
1、遞歸法
直接上代碼:
//recursion class Solution1 { public:vector<int> postorderTraversal(TreeNode* root) {vector<int> ret;postHelper(ret,root);return ret;} private:void postHelper(vector<int>& ret,TreeNode* root){if(root==NULL)return;postHelper(ret,root->left);postHelper(ret,root->right);ret.push_back(root->val);} };2、迭代法
迭代法使用一個棧來保存當前不需要訪問的節點。不過,不同于中序遍歷與前序遍歷,在后序遍歷中每一個節點需要一個標志位,來標識當前節點的左右子樹是否被訪問。因為在后序遍歷中,只有一個節點的左右子樹被訪問后它才能被訪問。因此,壓入棧中的數據類型需要是一個pair<TreeNode*,int>,其中用1來表示當前節點的左右子樹正被訪問,當再次訪問到此節點時可以訪問此節點;用0表示當前節點的左右子樹未被訪問,再次訪問到此節點時需要首先訪問此節點的左右子樹。代碼如下:
//iteration class Solution1 { public:vector<int> postorderTraversal(TreeNode* root) {vector<int> ret;if(root==NULL)return ret;stack<pair<TreeNode*,int>> st;st.push(make_pair(root,0));while(!st.empty()){TreeNode *curr=st.top().first;if(st.top().second==1){ret.push_back(curr->val);st.pop();}else{st.top().second=1;if(curr->right)st.push(make_pair(curr->right,0));if(curr->left)st.push(make_pair(curr->left,0));}}return ret;} };例如,對于如下的二叉樹,運行過程如下:
(1)節點1
棧中內容(左側是TreeNode*,這里用節點內容表示,右側是標志位):
| ? | ? |
| ? | ? |
| 1 | 0 |
(2)節點1
標志位是0,不能訪問當前節點,添加右兒子和左兒子,同時將標志位置為1:
棧中內容:
| 2 | 0 |
| 3 | 0 |
| 1 | 1 |
(3)節點2
標識位是0,不能訪問,添加右兒子和左兒子。由于均為空,不添加,將標志位置為1;
棧中內容:
| 2 | 1 |
| 3 | 0 |
| 1 | 1 |
(4)節點2
標志位是1,可以訪問,訪問后彈出。
棧中內容:
| ? | ? |
| 3 | 0 |
| 1 | 1 |
(5)節點3
標志位是0,不能訪問,添加子節點。并將標志位置為1;
棧中內容:
| 4 | 0 |
| 3 | 1 |
| 1 | 1 |
(5)節點4
標志位是0,不能訪問,添加子節點并將標志位置為1;
棧中內容:
| 4 | 1 |
| 3 | 1 |
| 1 | 1 |
(6)節點4
標志位是1,可以訪問,訪問后彈出;
棧中內容:
| ? | ? |
| 3 | 1 |
| 1 | 1 |
(7)節點3
標志位是1,可以訪問,訪問后彈出;
棧中內容:
| ? | ? |
| ? | ? |
| 1 | 1 |
(8)節點1
標志位是1,可以訪問,訪問后彈出;
棧中內容:
| ? | ? |
| ? | ? |
至此,棧為空,循環結束。可以看到,這種方式每個節點需要訪問兩次。
3、迭代法:按照根、右、左的順序訪問然后取反
這種方法就是按照根、右、左的順序訪問,然后將結果取反即可。后序遍歷的順序是左、右、根。這種方法就可以在前序遍歷的基礎上修改即可。代碼如下:
class Solution3 { public:vector<int> postorderTraversal(TreeNode* root) {vector<int> ret;if(root==NULL)return ret;stack<TreeNode*> st;st.push(root);while(!st.empty()){TreeNode *curr=st.top();st.pop();if(curr->left)st.push(curr->left);if(curr->right)st.push(curr->right);ret.push_back(curr->val);}reverse(ret.begin(),ret.end());return ret;} };在按照根、右、左的順序遍歷時,每個節點訪問一遍,最后將結果取反時,每個節點也訪問一遍,因此節點的總訪問次數和方法2相同。4、Morris方法
后序遍歷的Morris方法思路比較難。但整體上還是一樣的,對原來的二叉樹的修改也是一樣的,不同的是訪問的順序。而在后序遍歷中,訪問時比較麻煩。下面是整個算法的工作過程;
首先建立一個臨時節點dump,令其左兒子是root。并且還需要一個子過程,就是倒序輸出某兩個節點之間路徑上的各個節點。
步驟:
當前節點設置為臨時節點dump。
(1)如果當前節點的左兒子為空,則將其右兒子作為當前節點;
(2)如果當前節點的左兒子非空,在當前節點的左子樹中找到當前節點在中序遍歷下的前驅節點;
?? a) 如果前驅節點的右孩子為空,將它的右兒子設置為當前節點。當前節點更新為當前節點的左兒子;
?? b) 如果前驅節點的右兒子為當前節點,將它的右孩子重新設為空。倒序輸出從當前節點的左兒子到該前驅節點這條路徑上的所有節點。當前節點更新為當前節點的右兒子;
(3)重復以上(1)(2)直到當前節點為空。
代碼如下:
//morris class Solution4 { public:vector<int> postorderTraversal(TreeNode* root) {vector<int> ret;TreeNode *dump=new TreeNode(0);dump->left=root;TreeNode *curr=dump;TreeNode *pre;while(curr){if(curr->left==NULL){curr=curr->right;}else{pre=curr->left;while(pre->right&&pre->right!=curr)pre=pre->right;if(pre->right==NULL){pre->right=curr;curr=curr->left;}else{reverseAddNodes(curr->left,pre,ret);pre->right=NULL;curr=curr->right;}}}return ret;} private:void reverseAddNodes(TreeNode *begin,TreeNode *end,vector<int>& ret){reverseNodes(begin,end);TreeNode *curr=end;while(true){ret.push_back(curr->val);if(curr==begin)break;curr=curr->right;}reverseNodes(end,begin);}void reverseNodes(TreeNode *begin,TreeNode *end){TreeNode *pre=begin;TreeNode *curr=pre->right;TreeNode *post;while(pre!=end){post=curr->right;curr->right=pre;pre=curr;curr=post;}} };總結
以上是生活随笔為你收集整理的二叉树后序遍历的四种方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《环球冒险》20号几点开服啊?
- 下一篇: 子宫内膜厚度影响怀孕吗