树的前序和中序遍历我们都已经写完了,现在我们来写一下树的后序遍历的非递归写法。
我自己的思路:
一些基础的知识可以参看我之前的前序遍历的文章。总之我稍微模拟了一下,得出了以下重要结论:
- 首先这个过程有点复杂,我们可能得用一个循环来多次执行,因为是不定次数的循环,所以我们选择while。
- 如何实现这个while循环?我的一个基本思路就是每次循环体只处理一个状态,然后转换成另一个状态,交给下一次的while循环。
- 什么时候while循环结束呢?由于是后序遍历,根节点必然是最后出栈的,所以当我们把根节点出栈时,也就结束了。
循环体内每次要执行的东西实现思路如下:
- 先找最左边的节点,统统入栈。
- 利用一个while循环回溯完 有右子树,或者左右节点都被访问过了的节点,将其出栈,并将结果输出。
- 将指针指向右子树。留给下一次循环。
自己的代码:
/**
* 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) {
vector<int> result;
stack<TreeNode *> s;
set<TreeNode *> hash; // 用来记录访问过的叶子节点(只有叶子节点访问完了,才能回溯到其父节点)
hash.insert(NULL);
// 先处理特殊情况
if (!root) { return result; }
// 处理根节点
s.push(root);
auto p = root->left;
hash.insert(p);
// 开始搜索
while (!s.empty()) {
// 寻找最左边的节点
while (p) {
s.push(p);
p = p->left;
hash.insert(p); // 这些左边的节点,我都已经访问过了
}
// 不断回溯,直到找到可以搜寻右子树的节点
p = s.top();
while (!s.empty() && (hash.count(p->left) && hash.count(p->right) || !p->right)) {
// 左右子树都被搜寻过,或者右子树为空,才会被记录值并pop
result.push_back(p->val);
s.pop();
if (!s.empty()) p = s.top(); // 先pop一定要注意为空的情况
}
p = p->right;
hash.insert(p);
}
return result;
}
};
反思:
由于引入了hash用来记录节点是否被访问过,程序的编写难度直线上升。事实上,完全不需要引入hashtable,网上有很多不错的解法,我下面贴一个我自认为较好的。
大犇的思路:
基本上差不多,但是代码的层面上做了不少改进,如:
- 最外层的while循环使用了p || stk.size();这样我们在前面不需要对根节点做特殊处理了(不然第一次就进不去循环)。
- 主函数体仅仅用了一个while内套一对嵌套的if,这提示我们——有时候不需要在while循环再套一层循环,可以看看利用外层的while循环可不可以达到这个效果。
- 没有使用所谓的Hash表,仅仅记录了上一次访问的节点,降低了复杂度。
还有一些技巧,比如只检测右节点有没有被访问过,这是因为我们最先访问的就是左节点。
大犇的代码:
/**
* 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) {
vector<int>res;
stack<TreeNode*>stk;
TreeNode* p=root,*q=NULL;
while(p||stk.size()){
// 寻找最左的节点
if(p){
stk.push(p);
p=p->left;
}else{
p=stk.top();
// 看看能不能进入右子树(特别地,当已经访问过时,也不能访问右子树)
if(p->right&&p->right!=q)p=p->right;
else{ // 输出结果,保存当前访问过的节点
p=stk.top();
stk.pop();
res.push_back(p->val);
q=p;
p=NULL;
}
}
}
return res;
}
};