先序
对于任一结点p:
1)访问结点p,并将结点p入栈;
2)判断结点p的左孩子是否不空先while循环直到空,这其间要输出根节点值(因为是先序);这之后,取栈顶结点并进行出栈操作,令 p = 栈顶元素的右孩子(如果不为空循环,如果为空证明遍历完毕,会再取栈顶元素);
3)直到p为NULL并且栈为空,则遍历结束。
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> s;
TreeNode* p = root;
vector<int> res;
while(p!=nullptr || !s.empty()){
while(p != nullptr){
res.push_back(p->val);
s.push(p);
p = p->left;
}
p = s.top();
s.pop();
p = p->right;
}
return res;
}
};
中序
同前序,但因为是先输出左结点再输出根结点,所以要在每次取栈顶元素后进行输出。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> s;
TreeNode* p = root;
vector<int> res;
while(p!=nullptr || !s.empty()){
while(p != nullptr){
s.push(p);
p = p->left;
}
p = s.top();
res.push_back(p->val);
s.pop();
p = p->right;
}
return res;
}
};
后序
因为后序的顺序是先访问左子树,再访问右子树,最后访问根节点。当用栈来存储节点,必须分清返回根节点时,是从左子树返回的,还从右子树返回的。所以,使用辅助指针r,其指向最近访问过的节点(也可以在节点中增加一个标志域,记录是否已被访问)。
取出栈顶元素之后,如果当前结点p的右结点存在且右结点不是r,证明该结点的左子树应该刚刚遍历完毕,而右子树还没有遍历,所以入栈右结点,开始遍历右子树;若p的右结点为空或者右结点就是r,则证明左子树和右子树都遍历了(空算作已遍历),此时该输出根结点了,出栈,令 r = 最近访问结点(也就是p),再将p置空(这里是为了是下次循环 while(p!=nullptr) 能直接出来以防止重复入栈)。
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> s;
TreeNode* p = root;
TreeNode* r = nullptr;
vector<int> res;
while(p!=nullptr || !s.empty()){
while(p != nullptr){
s.push(p);
p = p->left;
}
p = s.top();
if(p->right && p->right != r){
p = p->right;
}else{
res.push_back(p->val);
s.pop();
r = p;
p = nullptr;
}
}
return res;
}
};