二叉树先序中序后序非递归遍历总结,尤其是后序的几种非递归遍历很值得一看

二叉树的非递归遍历

上篇文章 二叉树的前序中序后序三种遍历方式介绍
给出了三种遍历的递归方法,递归方法虽然简洁,但可读性一般不好执行效率一般也不高。本篇文章就将递归方法变成非递归方法。
完整的可调试代码可看这里:二叉树全部操作的代码及其注释(从二叉树的建立,到七种遍历方式,再到各种操作)

非递归算法实现的基本思路:使用堆栈
具体实现的三种遍历方法如下所示:



非递归中序遍历二叉树

中序遍历非递归遍历算法

  1. 遇到一个结点,就把它压栈,并去遍历它的左子树;
  2. 当左子树遍历结束后,从栈顶弹出这个结点并访问它
  3. 然后按其右指针再去中序遍历该结点的右子树。

代码如下:

//非递归方法中序遍历二叉树
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; // 转向右子树 		
	}
}

非递归前序遍历二叉树

前序遍历非递归遍历算法

  1. 遇到一个结点,就把它压栈,同时访问它
  2. 接着循环去遍历它的左子树;
  3. 当左子树遍历结束时,按其右指针再去前序遍历该结点的右子树。

代码如下:

//非递归方法前序遍历二叉树
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. 难点分析:
    后序遍历与前序遍历和中序遍历不同,不能用栈直接解决;原因在于对于同一个结点它会两次处于栈顶:
    1.左子树后序遍历完毕后
    2.右子树后序遍历完毕后
    结点的访问及出栈应该是在第二次处于栈顶的时候。

  2. 关键点:
    所以关键点就是:如何判断处于栈顶的元素是第一次处于栈顶还是第二次处于栈顶

  3. 解决方法

  • 这里用到是标记法
  • 主要思想:做一个标记,根据标记,判断是结点是第几次处于栈顶。
  • 怎么标记呢?
    方法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.我好像在某个地方看到可以用队列,不太记得了又懒得去想了,就留给聪明的你去想了,评论区等待你大展身手了。^ ~ ^

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值