今天在刷Leetcode的二叉樹卡片,首先碰到的就是二叉樹的三種遍歷方法,遞歸方法很簡單,但是要求用迭代的方法來寫的話就沒那麼容易了,在題解區裏看到了一個大神的模板化迭代寫法,非常有新意,於是在這裏寫點自己的理解
原文鏈接如下:
理論上來說,所有遞歸的算法都是可以用非遞歸來實現的,因爲遞歸本身就是操作系統調用棧來保存函數的入口地址,遇到return之後返回到入口,因此用非遞歸方法寫遞歸程序最核心的部分就是
1. 如何“保護現場”,在二叉樹的遍歷這裏,原答案用到了一個nullptr空指針來保存當前訪問但又不輸出的那個節點(下面代碼中的temp節點),也就是說,第二次遇到同一個節點的時候,意味着這個節點的前驅已經遍歷過了,因此可以放心大膽地輸出當前節點的value
2. 由於棧是FILO先進後出的結構,這就跟遞歸的順序要相反,也就是說我們正常後序遍歷的順序是 left->right->mid,在棧這裏就要mid->right->left的順序入棧,出棧的時候才能按照正常遞歸的順序
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
if(!root) return {};
vector<int>ans;
stack<TreeNode*>mystack; //用棧來保存遍歷的節點
mystack.push(root);
while(!mystack.empty())
{
auto temp = mystack.top();
mystack.pop();
if(temp)
{
mystack.push(temp); //此題爲後序遍歷,因此入棧順序要反過來,爲中->左->右
mystack.push(nullptr);
if(temp->right) mystack.push(temp->right);
if(temp->left) mystack.push(temp->left);
}
else
{
ans.push_back(mystack.top()->val);
mystack.pop();
}
}
return ans;
}
};
上面是本人蔘照鏈接自己寫的後序遍歷的一個案例,先序遍歷和中序遍歷只需要將if(temp)裏的入棧順序作相應調整即可
另外此類題還有官方題解提到的莫里斯遍歷,核心的部分也是確認當前節點的前驅是否已遍歷,實現O(1)的空間複雜度,這裏先佔個坑,後面自己看懂了再來寫感想