1.题目
给定二叉树,返回其节点值的后序遍历序列。
2.思路
后序遍历的过程:
(1) 后序遍历根节点的左子树
(2) 后序遍历根节点的右子树
(3) 访问根节点
后序遍历的非递归实现是三种遍历方式中最难的一种。因为在后序遍历中,要保证左子节点和右子结点都已被访问并且左子节点在右子节点前访问才能访问根结点,这就为流程的控制带来了难题。
对于任一结点P,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左子结点的结点,此时该结点出现在栈顶,但是此时不能将其出栈并访问, 因此其右孩子还为被访问。所以接下来按照相同的规则对其右子树进行相同的处理,当访问完其右孩子时,该结点又出现在栈顶,此时可以将其出栈并访问。这样就保证了正确的访问顺序。可以看出,在这个过程中,需要判断结点的右子树是否为空,或者是否已经被访问过了,如果为空或者已经被访问过了,此时才可以访问该节点。
因此需要添加一个有关右子树的标识pre,表示前一个被访问过的节点,如果pre 为空,或者pre 是当前节点的右子节点,那么现在可以访问该节点。
3.实现
3.1 非递归
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
if(root == nullptr)
return result;
stack<TreeNode*> s;
TreeNode* pre = nullptr;
TreeNode* cur = root;
while(cur != nullptr || !s.empty())
{
if(cur != nullptr)
{
s.push(cur);
cur = cur->left;
}
else
{
cur = s.top();
if(cur->right == nullptr || pre == cur->right)
{
// 如果右子节点为空,或者已经被访问过了——之前访问的节点就是右子节点
s.pop();
result.push_back(cur->val);
pre = cur;
cur = nullptr;
}
else
{
cur = cur->right;
}
}
}
return result;
}
};
3.2 递归版本1
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
postorder(root, res);
return res;
}
void postorder(TreeNode* root, vector<int>& res)
{
//函数作用: 后序遍历以root为根节点的数,按顺序存入res中
if(root == nullptr)
return;
postorder(root->left, res);
postorder(root->right, res);
res.push_back(root->val);
}
};
3.3 递归版本2
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
if(root == nullptr)
return res;
vector<int> leftTree = postorderTraversal(root->left);
vector<int> rightTree = postorderTraversal(root->right);
res.insert(res.end(), leftTree.begin(), leftTree.end());
res.insert(res.end(), rightTree.begin(), rightTree.end());
res.push_back(root->val);
return res;
}
};