今天是释然发题解的第三十天,以后会经常和大家分享学习路上的心得,希望和大家一起进步,一起享受coding的乐趣
本文约2100字,预计阅读7分钟
很多天前我们学习了二叉树,忘记的小伙伴们可以看一下哦:
二叉树
今天我们来聊一聊二叉树的后序遍历,明天和大家分享线段树问题的相关题目:
非递归的后序遍历
实现
我们想一下假如说一棵树是这样的:
[
3,9,20,null,null,15,7
]
这棵树应该长这样:
3
/ \
9 20
/ \
15 7
二叉树的后序遍历是比较复杂的,复杂在哪呢?
最重要的问题就是,节点的值该何时打印的问题。对于前序遍历来说,每一次直接打印然后找右子树和左子树,不停地打印栈顶元素就行。对于中序遍历来说,如果左子树始终有结点存在,那么第一个输出的结点门就应该是最左下角的那个,所以当我们把根节点压入栈的时候,我们就需要不断地把左边界压入栈,即不停地
temp=temp.left;
直到找到的那个结点为空为止,这个时候就弹出这个结点,开始找这颗子树的右结点的左子树(虽然讲的非常变扭,其实自己画个图就好)然后不停地找左子树
为什么要不停地找左子树呢?很简单,中序遍历最先需要遍历的就是左子树的左孩子,但是一个结点对于自己的左孩子来说其实就是父亲,不能第一个打印,因此必须不停地找左孩子
最后栈为空时,停止
那么对于后序遍历来说,同样是根节点压栈,但是什么时候打印根节点呢?
我一开始是这样想的,如果也是先压右节点,再压左节点,这样是符合遍历的顺序的,但是什么时候开始打印呢?应该是根节点左子树的最左边的叶子结点开始打印.既然这样,我们就先找到那个第一个需要打印的结点,然后从栈里面打印就行。
其实这是不合理的,当我们先压右节点,再压左节点的时候,此时再想压右节点的孩子就不方便了。这个时候,我们有两种解决方案:
第一个就是再开一个栈,我们去改变它的压入的顺序,先不停地压左节点,再压右节点,每一次弹出到第二个栈里面,就可以保证所有的结点都可以找到,然后利用第二个栈去打印。
第二个就是,有标记的去压栈,现在最麻烦的就是这个结点的左孩子和右孩子有没有打印过,我们知道只要根节点打印过了,那么就不需要去找这个结点的左右孩子了。相当于额外使用一个指针,代表上一次打印的结点。
实现
//非递归的后序遍历
//选择两个栈的方法
#include<stack>
void PosOrder(Node root)
{
if (root == null)
{
return;
}
stack<Node> stack1;
stack<Node> stack2;
//vector<int> result;
// 先把根节点压栈
stack.push(root);
Node cur;
while (!stack1.empty())
{
cur=stack.pop();
stack2.push(cur);
//root = stack.top();
//cout<<root.val;
// 先压左节点
if (root.left != NULL)
{
stack.push(root.left);
//root = root.right;
}
// 再压右节点
if (root.right != NULL)
{
stack.push(root.right);
}
}
while(!stack2.empty())
{
cout<<stack2.pop();
}
cout<<" "<<endl;
}
//二叉树的后序遍历
//只使用一个栈的情况
#include <stack>
void PosOrder(Node root)
{
if (root == NULL )
{
return;
}
stack<Node> sta;
sta.push(root);
Node cur=NULL;
//root 是上一次打印过的结点
//cur 是当前的栈顶结点
while(!sta.empty())
{
cur=sta.top();//这个时候不能弹出,因为不知道
//这个结点的左子树和右子树是否需要压入栈中
//考虑左子树是否需要压栈
if (cur.left != NULL && root!=c.left && h!=cur.right)
{
sta.push(cur.left);
}
//考虑右子树是否需要压栈
else if(c.right != NULL&& h!=cur.right)
{
sta.push(cur.right);
}
else
{
cout<<cur.value;
sta.pop();
h=cur;
}
}
printf("\n");
}
这个过程不好想,看了好久才弄懂,哎慢慢来,以后精力就在这方面了。
好了,今天的二叉树的后序遍历就到这里。
释然每天发布一点自己学习的知识,希望1年后我们也能在ACM的赛场上见面,一起去追寻自己的程序猿之路吧!
后期也会和大家一起分享学习心得和学习经验呢,明天我们不见不散哦!
近期预告:
线段树、树状数组、状压DP、Trie树、图论
如果大家有什么建议或者要求请后台留言
联系方式:shirandexiaowo@foxmail.com