Leetcode 145. 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?

解题思路:
题目要求我们实现用迭代的方式后序遍历二叉树,这时候就需要使用到栈的数据结构, 我们基本的思想就是让他不断的向左走, 直到走到树的最左端, 然后回溯。 但是问题来了, 在回溯的过程中, 我如何才能判断我的右子树是否已经被遍历过了呢, ie,我应该在什么时候访问这个节点最合适?
想到这里, 我们想到图的遍历过程中, 有一个表记录着被访问过的节点的信息, 那么, 同样的我们也可以参考这样的思想, 用一个set结构记录节点的访问信息, 至此算法就出来了

下面是我们的代码部分:

/**
 * Definition for binary tree
 * 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) {
        stack<TreeNode *> mystack;
        set<TreeNode *> isvisited;
        vector<int> res;

        while (!mystack.empty() || root){
            // 如果节点非空, 压栈
            if (root){
                mystack.push(root);
                root = root->left;
            }
            else{
                // 只有当左子树遍历完了, 才到这里
                TreeNode * top = mystack.top();
                if (top->right == nullptr || isVisited(isvisited, top->right)){
                    // 右子树为空或者右子树已经被遍历过了, 更新访问表, 并输出
                    res.push_back(top->val);
                    isvisited.insert(top);
                    mystack.pop();
                }
                else{
                    // 右子树没有被遍历过, 需要对右子树遍历
                    root = top->right;
                }
            }
        }

        return res;
    }

private:
    inline bool isVisited(set<TreeNode *> & myset, TreeNode * data){
        return (myset.find(data) == myset.end()) ? false : true;
    }
};

然后我们查看了一下, 我们3个月前提交的代码, 发现思路是一样的, 不过当时是采用定义一个新的数据结构来记录左右子树的访问与否。

struct stackNode{
        TreeNode * node;
        bool right;
        bool left;

        stackNode(TreeNode * x, bool bleft, bool bright) : node(x), left(bleft), right(bright){}
    };

相当于将原来的数据结构又做了一层封装。
代码如下:

//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 {
private:
    struct stackNode{
        TreeNode * node;
        bool right;
        bool left;

        stackNode(TreeNode * x, bool bleft, bool bright) : node(x), left(bleft), right(bright){}
    };

public:
    vector<int> postorderTraversal(TreeNode* root) {
        if (root == nullptr)
            return vector<int>();

        vector<int> res;
        stack<stackNode> mystack;

        while (root || !mystack.empty()){
            if (root){
                mystack.emplace(stackNode(root, true, false));
                root = root->left;
            }
            else{
                stackNode tmp = mystack.top();
                root = tmp.node;
                mystack.pop();
                if (tmp.right == false){
                    mystack.emplace(stackNode(tmp.node, true, true));
                    root = root->right;
                }
                else{
                    res.push_back(tmp.node->val);
                    root = nullptr;
                }
            }
        }

        return res;
    }
};

网上有大神的解法, 不需要使用访问表, 基本思想:
记录上次回溯的节点, 此时由于需要回溯, root == nullptr, 然后判断这个节点是否是当前栈顶节点的右节点, 就可以判断出当前节点的右子树是否已经被遍历过了。 特别巧妙的一种方法!!

vector<int> postorderTraversal(TreeNode* root) {
    vector<int> nodes;
    stack<TreeNode*> toVisit;
    TreeNode* curNode = root;
    TreeNode* lastNode = NULL;
    while (curNode || !toVisit.empty()) {
        if (curNode) {
            toVisit.push(curNode);
            curNode = curNode -> left;
        }
        else {
            TreeNode* topNode = toVisit.top();
            if (topNode -> right && lastNode != topNode -> right)
                curNode = topNode -> right;
            else {
                nodes.push_back(topNode -> val);
                lastNode = topNode;
                toVisit.pop();
            }
        }
    }
    return nodes;
}

当然还有一种思路是使用 Morris 翻转, 这个算法没有仔细研究, 大家可以百度一下。

参考资料:
0 ms Clear C++ solutions — iterative, recursive, Morris traversal (3 different solutions!)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值