二叉树的非递归遍历
上篇文章 二叉树的前序中序后序三种遍历方式介绍
给出了三种遍历的递归方法,递归方法虽然简洁,但可读性一般不好执行效率一般也不高。本篇文章就将递归方法变成非递归方法。
完整的可调试代码可看这里:二叉树全部操作的代码及其注释(从二叉树的建立,到七种遍历方式,再到各种操作)
非递归算法实现的基本思路:使用堆栈
具体实现的三种遍历方法如下所示:
非递归中序遍历二叉树
中序遍历非递归遍历算法
- 遇到一个结点,就把它压栈,并去遍历它的左子树;
- 当左子树遍历结束后,从栈顶弹出这个结点并访问它;
- 然后按其右指针再去中序遍历该结点的右子树。
代码如下:
//非递归方法中序遍历二叉树
template<typename T> void inOrderNR(BiTree<T> *bt)
{
stack<BiTree<T>* > s; //创建栈
BiTree<T> *p = bt;
while(p || !s.empty())
{
while(p != NULL) /*一直向左并将沿途结点压入堆栈*/
{
s.push(p) ;
p = p->left;
}
visit(s.top());//访问根结点
p = s.top(); // p要指向此时s的栈顶元素,否则p=NULL,程序就会出错
s.pop(); //结点出栈
p = p->right; // 转向右子树
}
}
非递归前序遍历二叉树
前序遍历非递归遍历算法
- 遇到一个结点,就把它压栈,同时访问它;
- 接着循环去遍历它的左子树;
- 当左子树遍历结束时,按其右指针再去前序遍历该结点的右子树。
代码如下:
//非递归方法前序遍历二叉树
template<typename T> void preOrderNR(BiTree<T> *bt)
{
stack<BiTree<T>* > s;
BiTree<T> *p = bt;
while(p || !s.empty())
{
while(p)
{
s.push(p);
visit(p);
p = p->left;
}
p = s.top();
s.pop();
p = p->right;
}
}
非递归后序遍历二叉树
后序非递归遍历还是格外重要的,在求职升学面试中,也经常会问到后序的非递归遍历。
后序非递归遍历普遍认为它是非常的难的;但是弄清它的难点和关键点,其实它完全并没有想象的那么富复杂;
-
难点分析:
后序遍历与前序遍历和中序遍历不同,不能用栈直接解决;原因在于对于同一个结点它会两次处于栈顶:
1.左子树后序遍历完毕后
2.右子树后序遍历完毕后
结点的访问及出栈应该是在第二次处于栈顶的时候。 -
关键点:
所以关键点就是:如何判断处于栈顶的元素是第一次处于栈顶还是第二次处于栈顶 -
解决方法
- 这里用到是标记法
- 主要思想:做一个标记,根据标记,判断是结点是第几次处于栈顶。
- 怎么标记呢?
方法1.结构体绑定标志:
struct PostBiTree {
BiTree* TNode;
int flag;
};
// flag=1:第一次处于栈顶,不出栈不访问
// flag=2:第二次处于栈顶,此时访问该节点并出栈
方法2.建立一个指向出栈的前一节点的指针 : pre
如果出栈的前一节点pre是栈顶结点元素的右节点或者该节点没有右节点 , 就可以访问该节点了
下面的代码是用到这种方法:
//如果出栈的前一节点pre是栈顶结点元素的右节点
//或者该节点没有右节点,就可以访问该节点了
if(pre == p->right || p->right == NULL)
{
visit(p);
········
}
具体代码还请参照下面,有不懂欢迎提问哦! ^ - ^
4. 参考代码:
//非递归方法后序递归二叉树
template<typename T> void postOrderNR(BiTree<T> *bt){
stack<BiTree<T>* > s;
BiTree<T> *p = bt;
BiTree<T> *pre = nullptr; //增加一指向前一结点的指针
while(p || !s.empty())
{
while(p != NULL) /*一直向左并将沿途结点压入堆栈*/
{
s.push(p) ;
p = p->left;
}
p = s.top();
if(pre == p->right || p->right == NULL) //如果栈顶结点的右
{
visit(p);
pre = p; //pre指向上一次访问的结点
s.pop();
p = nullptr; //这里的p如果直接指向栈顶元素的有结点(即p=s.top())
//将会导致循环出不去,也就是到最后一个
}
else
{
p = p->right;
}
}
}
当然除了上面的标记法还有其他的办法可以实现后序的非递归;下面再介绍几种:
1.先采用根右左的遍历方法,遍历二叉树并将结果存到栈中,再输出;这样就将结果翻转为左右根,也就是后序遍历啦!至于怎么样根左右遍历改一改前序遍历就可以了!
2.我好像在某个地方看到可以用队列,不太记得了又懒得去想了,就留给聪明的你去想了,评论区等待你大展身手了。^ ~ ^