深入理解后序遍历二叉树

起因

在看邓俊辉在学堂在线上的数据结构课的时候,发现前序/中序的二叉树迭代遍历讲的都非常好,偏偏不讲后序。看了书上的讲解后也觉得一头雾水,尤其是需要去找左侧最深可见叶子节点那里,我基本需要背诵代码的逻辑,非常痛苦。
所以开始在网上找有没有更好理解的逻辑去写这个后序遍历。

发现

我发现,大多数讲解后序遍历的博客多提到两个关键点,第一个是要关注:对于一个节点node,我们什么时候才对其进行访问?答案是在左右子树都访问完后,对node进行访问;或是左右子树都空时,这就等同于左右子树都访问完了。
第二个要关注的点是,后序遍历的访问次序是怎么样的?答案是,左|右|当前根节点。

基于以上的两个理解,我们不难发现,每次遇到一个节点,我们需要首先进行一个判断,当前这个节点是否满足访问条件?如果满足,对其进行访问,并且出栈(表示访问完成了)。如果不满足,那么我们先不要访问当前这个节点,(不要出栈),而是将当前节点的右/左子树根节点依次入栈,并且进入下一次循环。

思路

还是延续前序和中序的循环思路,尝试使用stack非空检查。根据第二个关键问题写push stack顺序,根据第一个关键问题写判断条件,可以写出简单粗暴的代码。遇到一个节点时,有三种状态:1)第一次遇到 2)从左子树返回 3)从右子树返回。
而对于一颗子树,有 [单左,单右,左右都有,左右都无],根据这四种情况写第一次遇到节点的判断条件。

实现1

vector<int> postorderTraversal(TreeNode* root) {
        vector<int> ret;
        if (!root)
            return ret;
        int left = 0, right = 0;
        vector<TreeNode *> stack = {root};
        TreeNode *prev = nullptr; // 记录上一个访问的节点,用于分辨从左子树返回还是右子树返回
        while (!stack.empty()) {
            auto node = stack.back();
            if (!node->left && !node->right) { // 第一次进入(叶子节点)
                ret.push_back(node->val);
                prev = node;
                stack.pop_back();
            }
            else if (node->left && node->right && prev != node->left && prev != node->right \ //左右都有,但既不是从左返回也不是从右返回
                || node->left && !node->right && prev != node->left \ //单左,但不是从左返回
                || node->right && !node->left && prev != node->right) { // 单右,但不是从右返回
                if (node->right)
                    stack.push_back(node->right);
                if (node->left)
                    stack.push_back(node->left);
                // continue;
            }
            else if (node->right && prev == node->right ){ //从右返回时,可直接访问
                ret.push_back(node->val);
                prev = node;
                stack.pop_back();
            }
            else if (!node->right && node->left && prev == node->left) { //从左返回且没右子树时,可直接访问
                ret.push_back(node->val);
                prev = node;
                stack.pop_back();
            }
            
        }
        return ret;

    }

实现2

vector<int> postorderTraversal(TreeNode* root) {
        if (!root) return vector<int> {};
        stack<TreeNode *> s;
        vector<int> res;
        
        s.push(root);
        TreeNode *prev = nullptr, *node;
        while (!s.empty()) {
            node = s.top();
            if (node->left == nullptr && node->right == nullptr || node->right && prev == node->right || node->left && node->left == prev && node->right == nullptr) {
                res.push_back(node->val);
                prev = node;
                s.pop();
            } else {
                if (node->right != nullptr) {
                    s.push(node->right);
                }
                if (node->left != nullptr)
                    s.push(node->left);
            }
        }
        return res;
    }
posted @ 2021-10-20 11:53  ijpq  阅读(172)  评论(0编辑  收藏  举报