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中弹出时,只检查右子树了,不用检查左边了。