主要采用压栈的方法
首先,后序遍历指的是 :先访问左子树,再访问右子树,最后访问根节点。
(后序,前序,中序,指的都是根节点的位次)
后序遍历(非递归)
后序遍历比较麻烦,需要经过两次压栈。(假设有n个节点)
- 我们要建立两个栈,每个栈的长度设置为n (保证空间足够,不过读者可以自行优化)
- 首先,将根节点压入,栈1,然后开始循环
(先看代码,不理解后面有解释,坚持,嘿嘿)
//格式如下
int j=0,i=1;
stack1[0]=root;
while(stack1[0])//保证栈 1 不为空,原因?,往下走
{
tree *p=stack1[--i];//因为你要弹栈,如果 stack 1 为空还弹啥。
stack2[j++]=p;//这一步就相当于,将栈 1 的栈顶元素弹出,并压入栈 2 中
stack[i]=0;//栈 1 中的栈顶元素弹出后
if(p->child[0])//判断左孩子节点是否存在
stack1[i++]=p->child[0];//将节点左孩子压入栈 1 中;
if(p->child[1])//判断右孩子节点是否存在。
stack1[i++]=p->child[1];//将节点右孩子压入栈 1 中,
}
经历上述循环后,我们可以得到,栈 2,它里面存储的是我们想要得到的后序遍历顺序。
so ! why
放个图,便于理解。
首先有表格展示(每一轮循环结束后的栈存储情况)
Stack 1 | Stack 2 |
---|---|
e,b | a |
e,d,c | a,b |
e,d | a,b,c |
e | a,b,c,d |
f,g | a,b,c,d,e |
f | a,b,c,d,e,g |
a,b,c,d,e,g,f |
嘿嘿,
我们可以通过大脑的小小运转,来理一理思路。
- 第一步,将根节点压入栈 1,在进入循环后,根节点弹出并被压入栈 2.
- 然后,将左孩子和右孩子依次进入栈1 ,在下一个循环中,进入栈2 的是右孩子。此时的p就是这个右孩子,讲他的左右孩子再度压入栈1 ,可以想到,按这样的情况下去,会一直将右孩子压入栈中,知道最右端。然后是右边的左孩子。
- 再将右子树的左右节点压入后,开始压入左子树,依旧是先右子树后左子树。
其实,虽然不是递归的形式,但还是存在递归的思想的。
在我们得到栈 2 后栈顶到栈底的遍历,也就说依次弹出,即后序遍历结果。