LeetCode:145. Binary Tree Postorder Traversal

在这里插入图片描述
总共想出了四种方法来做。
第一种是递归,不说了。后面集中都是迭代。

第二种是使用一个栈,而且借助一个set来记录哪些node是访问过它的子节点的,防止重复访问。缺点是需要额外的set的空间

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> ret;
        if (!root)
            return ret;
        stack<TreeNode*> st;
        st.push(root);
        set<TreeNode*> se;
        while (!st.empty()) {
            auto node = st.top();
            if (se.find(node) == se.end()) {
                se.insert(node);
                if (node->right)
                    st.push(node->right);
                if (node->left)
                    st.push(node->left);
            } else {
                st.pop();
                ret.push_back(node->val);
            }
        }
        return ret;
    }
};

第三种方法,与第二种方法相比,不使用额外的set方法来防止重复访问。如果我们仔细地画个图模拟一下后序遍历的话,会发现,如果访问的上一个节点是当前节点的一个子节点,就说明这个节点不需要再检查它的子节点了。

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> ret;
        if (!root)
            return ret;
        stack<TreeNode*> s;
        s.push(root);
        TreeNode* preNode = root;
        while (!s.empty()) {
            auto node = s.top();
            if (preNode == node->left || preNode == node->right || (!(node->left) && !(node->right))) {//需要注意最后一个条件
                s.pop();
                ret.push_back(node->val);
                preNode = node;
            } else {
                if (node->right)
                    s.push(node->right);
                if (node->left)
                    s.push(node->left);
                preNode = node;
            }
        }
        return ret;
    }
};

另外,需要额外注意注释的最后一个或的条件,如果当前节点没有左右子节点,那么就不去访问子节点了。如果没有这个条件的话,会造成死循环,到了最左下角的一个节点会无限循环。

第四种方法有意思了,原来我觉得我想出来的第三种方法已经够厉害了,但是评论区教我做人。
总的思想是这样的:前序遍历是“中,左,右”,后序遍历是“左,右,中”,那么如果我们使用一种稍微修改的“前序”也就是“中,右,左”,最后再把答案逆序,不就有了正确答案了吗?而且前序遍历的迭代要好写多了。

/**
 * 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> ret;
        if (!root)
            return ret;
        stack<TreeNode*> s;
        s.push(root);
        while (!s.empty()) {
            auto node = s.top();
            s.pop();
            ret.push_back(node->val);
            if (node->left)
                s.push(node->left);
            if (node->right)
                s.push(node->right);
        }
        reverse(ret.begin(), ret.end());
        return ret;
    }
};

我是真的服了,完全没想到有这种路子,有点骚。
这种方法某种程度上也算是“代码复用”吧,从一个已有的方法转变而来,是值得提倡的。
但是评论区也有人指出,这种方法不是严格的后序遍历,因为没有按照应有的顺序来访问节点。如果要按照后序对这棵树做某项任务,这么做是不行的。这种方法只是对于这条题目的一个正确解法而已。
这么一说,我的第三种方法才是最好的,哈哈哈~

另外,多说一句,这里前序和后序的迭代方法都有了,说说中序的迭代方法:
也是用一个stack,每次压进去一个node时候,都要把它的所有左边一溜儿都压进去。然后再从stack中弹出时,只检查右子树了,不用检查左边了。

posted @ 2019-08-20 19:25  于老师的父亲王老爷子  阅读(8)  评论(0编辑  收藏  举报