【树】二叉树的后序遍历——非递归写法

树的前序和中序遍历我们都已经写完了,现在我们来写一下树的后序遍历的非递归写法。

我自己的思路:

一些基础的知识可以参看我之前的前序遍历的文章。总之我稍微模拟了一下,得出了以下重要结论:

  • 首先这个过程有点复杂,我们可能得用一个循环来多次执行,因为是不定次数的循环,所以我们选择while
  • 如何实现这个while循环?我的一个基本思路就是每次循环体只处理一个状态,然后转换成另一个状态,交给下一次的while循环。
  • 什么时候while循环结束呢?由于是后序遍历,根节点必然是最后出栈的,所以当我们把根节点出栈时,也就结束了。

循环体内每次要执行的东西实现思路如下:

  1. 先找最左边的节点,统统入栈。
  2. 利用一个while循环回溯完 有右子树,或者左右节点都被访问过了的节点,将其出栈,并将结果输出。
  3. 将指针指向右子树。留给下一次循环。

自己的代码:

/**
 * 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,网上有很多不错的解法,我下面贴一个我自认为较好的。

大犇的思路:

基本上差不多,但是代码的层面上做了不少改进,如:

  1.  最外层的while循环使用了p || stk.size();这样我们在前面不需要对根节点做特殊处理了(不然第一次就进不去循环)。
  2. 主函数体仅仅用了一个while内套一对嵌套的if,这提示我们——有时候不需要在while循环再套一层循环,可以看看利用外层的while循环可不可以达到这个效果。
  3. 没有使用所谓的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;
    }
};

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值