题目描述:
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?
解题思路:
题目要求我们实现用迭代的方式后序遍历二叉树,这时候就需要使用到栈的数据结构, 我们基本的思想就是让他不断的向左走, 直到走到树的最左端, 然后回溯。 但是问题来了, 在回溯的过程中, 我如何才能判断我的右子树是否已经被遍历过了呢, ie,我应该在什么时候访问这个节点最合适?
想到这里, 我们想到图的遍历过程中, 有一个表记录着被访问过的节点的信息, 那么, 同样的我们也可以参考这样的思想, 用一个set结构记录节点的访问信息, 至此算法就出来了。
下面是我们的代码部分:
/**
* Definition for binary tree
* 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) {
stack<TreeNode *> mystack;
set<TreeNode *> isvisited;
vector<int> res;
while (!mystack.empty() || root){
// 如果节点非空, 压栈
if (root){
mystack.push(root);
root = root->left;
}
else{
// 只有当左子树遍历完了, 才到这里
TreeNode * top = mystack.top();
if (top->right == nullptr || isVisited(isvisited, top->right)){
// 右子树为空或者右子树已经被遍历过了, 更新访问表, 并输出
res.push_back(top->val);
isvisited.insert(top);
mystack.pop();
}
else{
// 右子树没有被遍历过, 需要对右子树遍历
root = top->right;
}
}
}
return res;
}
private:
inline bool isVisited(set<TreeNode *> & myset, TreeNode * data){
return (myset.find(data) == myset.end()) ? false : true;
}
};
然后我们查看了一下, 我们3个月前提交的代码, 发现思路是一样的, 不过当时是采用定义一个新的数据结构来记录左右子树的访问与否。
struct stackNode{
TreeNode * node;
bool right;
bool left;
stackNode(TreeNode * x, bool bleft, bool bright) : node(x), left(bleft), right(bright){}
};
相当于将原来的数据结构又做了一层封装。
代码如下:
//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 {
private:
struct stackNode{
TreeNode * node;
bool right;
bool left;
stackNode(TreeNode * x, bool bleft, bool bright) : node(x), left(bleft), right(bright){}
};
public:
vector<int> postorderTraversal(TreeNode* root) {
if (root == nullptr)
return vector<int>();
vector<int> res;
stack<stackNode> mystack;
while (root || !mystack.empty()){
if (root){
mystack.emplace(stackNode(root, true, false));
root = root->left;
}
else{
stackNode tmp = mystack.top();
root = tmp.node;
mystack.pop();
if (tmp.right == false){
mystack.emplace(stackNode(tmp.node, true, true));
root = root->right;
}
else{
res.push_back(tmp.node->val);
root = nullptr;
}
}
}
return res;
}
};
网上有大神的解法, 不需要使用访问表, 基本思想:
记录上次回溯的节点, 此时由于需要回溯, root == nullptr, 然后判断这个节点是否是当前栈顶节点的右节点, 就可以判断出当前节点的右子树是否已经被遍历过了。 特别巧妙的一种方法!!
vector<int> postorderTraversal(TreeNode* root) {
vector<int> nodes;
stack<TreeNode*> toVisit;
TreeNode* curNode = root;
TreeNode* lastNode = NULL;
while (curNode || !toVisit.empty()) {
if (curNode) {
toVisit.push(curNode);
curNode = curNode -> left;
}
else {
TreeNode* topNode = toVisit.top();
if (topNode -> right && lastNode != topNode -> right)
curNode = topNode -> right;
else {
nodes.push_back(topNode -> val);
lastNode = topNode;
toVisit.pop();
}
}
}
return nodes;
}
当然还有一种思路是使用 Morris 翻转, 这个算法没有仔细研究, 大家可以百度一下。
参考资料:
0 ms Clear C++ solutions — iterative, recursive, Morris traversal (3 different solutions!)