[Leetcode] Binary Tree Postorder Traversal

来源:互联网 发布:域名具有什么属性 编辑:程序博客网 时间:2024/05/16 00:40

题目:

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?


Leetcode上并没有这道题,但是由于和inorder类似,所以写上来。

思路:递归的做法非常简单,重点是如何用循环来做。

        整体思路是利用一个堆栈去模拟递归的过程,问题是,如果完全按照递归的过程去做堆栈,那么对于栈顶的元素A需要将左子树压入堆栈,然后当左边的元素弹出堆栈后,再将右子树压入堆栈,最后弹出A。这样,元素A有三次成为栈顶的机会:第一次压入了左子树;第二压入右子树,第三次弹出,输出——递归的做法由于实际压入堆栈的是一个函数,很好的记录了当前状态(即进行到哪一步了);可是堆栈无法记录。

        解决的办法有很多,第一种是修改node的结构,但这一次bool成员无法满足要求了,因为需要记录三种状态,一是没压左子树的,需要压入左子树,二是压完左子树的,那么压入右子树,三是压完右子树的,那么输出并弹出;因此,需要用一个int去记录;第二种是利用一个hashmap去存上述状态;另外还有用两个栈去实现的做法。

        比较好的做法是,类似inorder遍历,设置current指针。但是现在的问题是,当A的左子树遍历完,A回到栈顶时并不能弹出,需要将右子树压入。这样,A有三次作为栈顶,而current指针只能帮助区分前两个状态,无法区分A是需要压入右子树还是需要输出并弹出。因此,需要另一个指针来帮助区分。具体做法是,利用另一个指针last_pop记录上一次弹出的元素,如果上一次弹出的元素是当前元素的右子节点,那么右子树已经被访问过了,可以输出跟节点并弹出堆栈;反之,将右子树压入堆栈。

        这里引入了几个新的问题,第一个是current指针的赋值问题。inorder遍历的时候,我们的做法是当current指向null时,说明栈顶的元素的左子树已经遍历完了,需要输出,出栈,同时将current指针导向右边继续向左向下走。可是,在做postorder的时候,栈顶元素的右子树可能没被访问过,那么把current指针导向右边,但栈顶元素的右子树也可能已经访问过,这个时候,current指针应该指向哪里呢?

        如果仔细观察inorder的算法,就可以发现,current指针的作用并不仅仅是指向当前要访问的节点这么简单,他的根本作用在于控制访问节点的方向——当current非空时,由上向下访问;反之,由下向上访问。就inorder遍历来说,当current向左向下走到头的时候,向上访问,栈顶的元素可以安全输出,然后将current指针导向右边,继续向下访问。现在就很容易理解postorder是如何运作的了:当current等于null时,向上访问到栈顶元素A,此时,如果A的右节点没有被访问过,那么将current指向这个节点继续向下访问,若果A的右节点已经被访问过了,那么我们将A输出,出栈后,需要继续向上访问,因此,current指针的值不会发生变化,依然是null.

        第二个问题是右子节点的判断问题。path.top()->right != last_pop是一个关键的条件:如果上一个弹出的节点不是右子节点,那么右子树很可能没有被访问过。有没有其他的条件呢?假设只有这一个条件,那么考虑一种情况:一个节点A只有左子节点B,没有右子节点。当B弹出堆栈后,A变成了栈顶。这时显然A的右子节点为null,并不为刚弹出的B,于是将current指向右边。下一次循环时,又会进入这条判断语句,current被再次赋值为null,造成死循环。造成这个死循环的根本原因是,右子树没有被访问过,于是赋值current,希望向下继续访问,而current正好是null,引导向上访问,于是程序就永远的停在了这个地方。因此,还需要加另一个判断条件:path.top()->right != nullptr,利用这个条件,我们保证了只有当右子节点不为空时,才进入这条语句,引导current,向下访问,否则,低于这个节点的所有节点已被访问完毕,输出这个节点,出栈,继续向上访问。


class Solution {public:    vector<int> postorderTraversal(TreeNode *root) {        vector<int> result;        if (root == nullptr) return result;        stack<TreeNode*> path;        TreeNode* current = root;        TreeNode* last_pop = nullptr;        while (!path.empty() || current != nullptr) {            if (current != nullptr) {   //keep pushing the left subtree                path.push(current);                current = current->left;            } else {   //until reach the end                if (path.top()->right != nullptr && path.top()->right != last_pop) {   //right subtree has not been touched                    current = path.top()->right;   //redirect to its right tree                } else {   //right subtree has been touched                    last_pop = path.top();                    path.pop();                    result.push_back(last_pop->val);                }            }        }        return result;    }};


总结:复杂度为O(n).

0 0
原创粉丝点击