目录
递归遍历相当于隐式的维护了一个栈,而非递归遍历则是模拟这个栈的实现。
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
前序遍历——根左右
思路
1.左路节点入栈,访问左路节点
2.访问左路节点的右子树
代码
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> ans;
TreeNode* cur=root;
while(!st.empty()||cur!=nullptr)
{
//1.访问左路节点,左路节点入栈,后续依次访问左路节点的右子树
while(cur!=nullptr)
{
ans.push_back(cur->val);
st.push(cur);
cur=cur->left;
}
//2.依次访问左路节点的右子树
TreeNode* top=st.top();
st.pop();//已经访问过的根节点pop,避免重复访问
cur=top->right;//用子问题的方法访问右子树
}
return ans;
中序遍历——左根右
思路
1.左路节点入栈
2.访问左路节点及其右子树
与前序遍历大致相同,但是左路节点的访问时间不一样。
前序的时候是在入栈的时候访问,中序是取出时访问。
本质
从栈顶取到一个节点,说明这个节点的左子树已经访问完了,
只需要访问它,在访问它的右子树。
代码
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> ans;
TreeNode* cur=root;
while(!st.empty()||cur!=nullptr)
{
//1.不访问左路节点,左路节点入栈
while(cur!=nullptr)
{
st.push(cur);
cur=cur->left;
}
//2.依次访问左路节点及其右子树
TreeNode* top=st.top();
st.pop();
ans.push_back(top->val);//访问左路节点
cur=top->right//访问右子树
}
return ans;
}
后序遍历——左右根
思路
与中序遍历相似,只是右子树的访问时间不同。
本质
从栈顶取到一个节点,说明这个节点的左子树已经访问完了,
只需要访问它的右子树,再访问它。
但是这会出现一个节点访问两次的情况,如下图所示:
为了实现不同情况下指向该节点时实现的不同操作,我们引入一个指针prev,该指针始终指向访问(即push到ans)的上一个节点。
具体流程如下
代码
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> ans;
TreeNode* cur=root;
TreeNode* prev=nullptr;
while(!st.empty()||cur!=nullptr)
{
//1.不访问左路节点,左路节点入栈
while(cur!=nullptr)
{
st.push(cur);
cur=cur->left;
}
//2.依次访问左路节点及其右子树
TreeNode* top=st.top();
//右子树访问完了 或
//上一个访问的节点是是右子树,
//那么访问该节点并出栈
if(top->right==nullptr||top->right==prev)
{
prev=top;
ans.push_back(top->val);
st.pop();
}
//继续访问该节点的右子树
else
{
cur=top->right;
}
}
return ans;
}