[LeetCode] Binary Tree Postorder Traversal 二叉樹的後序遍歷
Given a binary tree, return the postorder traversal of its nodes' values.
For example:
Given binary tree {1,#,2,3}
,
1 \ 2 / 3
return [3,2,1]
.
Note: Recursive solution is trivial, could you do it iteratively?
經典題目,求二叉樹的後序遍歷的非遞迴方法,跟前序,中序,層序一樣都需要用到棧,後續的順序是左-右-根,所以當一個節點值被取出來時,它的左右子節點要麼不存在,要麼已經被訪問過了。我們先將根結點壓入棧,然後定義一個輔助結點head,while迴圈的條件是棧不為空,在迴圈中,首先將棧頂結點t取出來,如果棧頂結點沒有左右子結點,或者其左子結點是head,或者其右子結點是head的情況下。我們將棧頂結點值加入結果res中,並將棧頂元素移出棧,然後將head指向棧頂元素;否則的話就看如果右子結點不為空,將其加入棧,再看左子結點不為空的話,就加入棧,注意這裡先右後左的順序是因為棧的後入先出的特點,可以使得左子結點先被處理。下面來看為什麼是這三個條件呢,首先如果棧頂元素如果沒有左右子結點的話,說明其是葉結點,而且我們的入棧順序保證了左子結點先被處理,所以此時的結點值就可以直接加入結果res了,然後移出棧,將head指向這個葉結點,這樣的話head每次就是指向前一個處理過並且加入結果res的結點,那麼如果棧頂結點的左子結點或者右子結點是head的話,說明其子結點已經加入結果res了,那麼就可以處理當前結點了,程式碼如下:
解法一:
class Solution { public: vector<int> postorderTraversal(TreeNode* root) { if (!root) return {}; vector<int> res; stack<TreeNode*> s{{root}}; TreeNode *head = root; while (!s.empty()) { TreeNode *t = s.top();if ((!t->left && !t->right) || t->left == head || t->right == head) { res.push_back(t->val); s.pop(); head = t; } else { if (t->right) s.push(t->right); if (t->left) s.push(t->left); } }return res; } };
由於後序遍歷的順序是左-右-根,而先序遍歷的順序是根-左-右,二者其實還是很相近的,我們可以先在先序遍歷的方法上做些小改動,使其遍歷順序變為根-右-左,然後翻轉一下,就是左-右-根啦,翻轉的方法我們使用反向Q,哦不,是反向加入結果res,每次都在結果res的開頭加入結點值,而改變先序遍歷的順序就只要該遍歷一下入棧順序,先左後右,這樣出棧處理的時候就是先右後左啦,參見程式碼如下:
解法二:
class Solution { public: vector<int> postorderTraversal(TreeNode* root) { if (!root) return {}; vector<int> res; stack<TreeNode*> s{{root}}; while (!s.empty()) { TreeNode *t = s.top(); s.pop(); res.insert(res.begin(), t->val); if (t->left) s.push(t->left); if (t->right) s.push(t->right); } return res; } };
那麼在Binary Tree Preorder Traversal中的解法二也可以改動一下變成後序遍歷,改動的思路跟上面的解法一樣,都是先將先序遍歷的根-左-右順序變為根-右-左,再翻轉變為後序遍歷的左-右-根,翻轉還是改變結果res的加入順序,然後把更新輔助結點p的左右順序換一下即可,程式碼如下:
解法三:
class Solution { public: vector<int> postorderTraversal(TreeNode* root) { vector<int> res; stack<TreeNode*> s; TreeNode *p = root; while (!s.empty() || p) { if (p) { s.push(p); res.insert(res.begin(), p->val); p = p->right; } else { TreeNode *t = s.top(); s.pop(); p = t->left; } } return res; } };
論壇上還有一種雙棧的解法,其實本質上跟解法二沒什麼區別,都是利用了改變先序遍歷的順序來實現後序遍歷的,參見程式碼如下:
解法四:
class Solution { public: vector<int> postorderTraversal(TreeNode* root) { if (!root) return {}; vector<int> res; stack<TreeNode*> s1, s2; s1.push(root); while (!s1.empty()) { TreeNode *t = s1.top(); s1.pop(); s2.push(t); if (t->left) s1.push(t->left); if (t->right) s1.push(t->right); } while (!s2.empty()) { res.push_back(s2.top()->val); s2.pop(); } return res; } };
類似題目:
參考資料: