三种非递归遍历都使用一种框架,代码如下
void traversal(TreeNode* root) {
TreeNode*p=root;
stack<TreeNode*>st;
while(!st.empty() || p!=NULL){
//将p及其左边一溜入栈
while(p!=NULL){
st.push(p);
p=p->left;
}
//p指向栈顶元素的右孩子,即使为空
p=st.top()->right;
//栈顶元素出栈
st.pop();
}
}
![](https://img-blog.csdnimg.cn/5258e0d563d643c5a2671dcc89c27d70.png)
中序遍历
其核心思想在于:每次把一棵树的根节点、根节点的左孩子、左孩子的左孩子…一直到最左下角,这一条斜线上的所有节点入栈。每次取出并打印栈顶节点的值,然后对其右子树进行上述入栈操作。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
if(!root)
return {};
stack<TreeNode*>st;
while(root){
st.push(root);
root=root->left;
}
vector<int>ans;
while(!st.empty()){
//取出栈顶元素
TreeNode*t=st.top();
st.pop();
ans.emplace_back(t->val);
//将栈顶元素右孩子一直到其最下角的左孩子全部入栈
TreeNode*p=t->right;
while(p){
st.push(p);
p=p->left;
}
}
return ans;
}
};
前序遍历
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
TreeNode*p=root;
vector<int>ans;
stack<TreeNode*>st;
while(!st.empty() || p!=NULL){
//将p及其左边一溜先打印,再入栈
while(p!=NULL){
//打印
ans.push_back(p->val); //cout<<st.top()->val
//入栈
st.push(p);
p=p->left;
}
//p指向栈顶元素的右孩子,即使它为空
p=st.top()->right;
//栈顶元素出栈
st.pop();
}
return ans;
}
};
后序遍历
后序遍历非递归的关键在于,栈顶元素何时打印,何时不打印。
当栈顶元素右子树全部访问完了(pre==top()->right
)或为空时,根据后序遍历的定义,此时可以打印栈顶元素;这里的pre指向上次打印的节点,由于后序遍历根最后打印,所以当pre==top()->right
时,能够判断出栈顶元素的右子树所有节点已经访问完毕。
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*>st;
TreeNode*pre=NULL;
vector<int>ans;
TreeNode*p=root;
while(p || !st.empty()){
while(p){
st.push(p);
p=p->left;
}
//如果栈顶元素的右子树为空 或者 右子树存在且已经全部访问过了,则直接打印
if(st.top()->right==NULL || st.top()->right==pre){
ans.push_back(st.top()->val);
pre=st.top();
st.pop();
p=NULL; //让下一轮直接访问栈顶元素
}else
p=st.top()->right;
}
return ans;
}
};