树的前中后序遍历(迭代)
struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
};
中序遍历(迭代法)
关键点:
- 非递归遍历要用到栈。
- 先遍历左子树,再访问根节点,最后遍历右子树,其中的转换遍历方向的条件是遍历节点为空(注意区分访问和遍历两个动词)。
- 左->根->右,方便记忆代码。
想法:
1.首先确定函数返回值和函数参数,将中序遍历的结果存入向量,参数为树的根节点。
2.迭代法就是按照思路来写代码,循环将左节点入栈,直至左节点为空,因此要有一个遍历用的节点p,初值为root,这一步的代码段为:
while(p)
{
s.push(p);
p = p->left;
}
3.如果p值为空了,说明这时左边已经访问完毕,输出当前空节点父节点(根),因为此节点已经入栈,且在栈顶,为了访问右子树,要将为空的p拉回来,p = s.top(),且要保证栈顶是我们要输出的“根”节点,输出过的直接pop掉,因此有如下代码段
p = s.top();
s.pop();
res.push_back(p->val);
4.接下来访问右子树,p = p->right,然后继续入栈,重复代码,如何将整段代码写入循环中,为了先完成入栈操作后,再输出以及访问右子树,以p是否为空作if else的条件,大循环下,由于需要输出完全,不单单因为p==NULL而结束,也不单单因栈空而结束,因为在输出root后,栈为空,但是p = p->right,仍然需要继续遍历(还一个好处可以省略root入栈)。
完整代码如下:
vector<int> Inorder(TreeNode* root,vector<int>& res){
if(!root) return {};
stack<TreeNode*>s;
TreeNode* p = root;
while(p || !s.empty())
{
//遍历左子树代码段
if(p)
{
s.push(p);
p = p->left;
}
//访问根节点和遍历右子树的代码段(因为右子树需要借助根节点遍历,所以整为一个代码块。
else
{
p = s.top();
s.pop();
res.push_back(p->val);
p = p->right;
}
}
}
## 前序遍历(迭代法)
关键点:
- 先访问根节点,再遍历左子树,最后遍历右子树
想法:
1.边遍历边访问,这就是有如下代码段的原因,这完成了根->左的部分。
if(p)
{
res.push_back(p->val);
s.push(p);
p = p->left;
}
2.然后借助入栈的部分,遍历右子树
else
{
p = s.top();
s.pop();
p = p->right;
}
完整代码:
vector<int> preOrdered(TreeNode* root,vector<int>& res)
{
if(!root) return{};
stack<TreeNode*>s;
TreeNode* p = root;
while(p || !s.empty())
{
if(p)
{
res.push_back(p->val);
s.push(p);
p = p->left;
}
else
{
p = s.top();
s.pop();
p = p->right;
}
}
}
后序遍历
关键点:
- 先遍历左子树,再遍历右子树,最后访问根节点
想法:
先遍历左子树,就是入栈操作,转向条件是当前遍历节点为空了,“左拐没路了”,然后通过栈顶元素遍历右子树,遍历右子树的情况是右节点不为空,且右节点未访问过,否则就输出跟节点,为了记录右边是否被访问过,需要一个指针。
完整代码
vector<int> postOrdered(TreeNode* root, vector<int>& res)
{
if(!p) return {};
stack<TreeNode*>s;
TreeNode* p = root, temp* = NULL;
while(p || !s.empty())
{
if(p)
{
s.push(p);
p = p->left;
}
else{
p = s.top();
if(p->right != NULL && p->right != temp)
{
p = p->right;
}
else
{
s.pop();
res.push_back(p->val);
temp = p;
p = NULL;
}
}
}
}