K:二叉树的非递归遍历
相关介绍:
二叉树的三种遍历方式(先序遍历,中序遍历,后序遍历)的非递归实现,虽然递归方式的实现较为简单且易于理解,但是由于递归方式的实现受其递归调用栈的深度的限制,当递归调用的深度超过限制的时候,会出现抛出异常的情况。为此,通过显示的使用栈的方式来实现二叉树遍历的非递归方式,其在使用上会更加的灵活。
运用下图对二叉树的三种遍历方式进行介绍:

后序遍历:
所谓的后序遍历是指对一棵二叉树按照左子树,右子树,根节点的顺序递归的在一棵二叉树的左右子树中访问相关节点的方式。如图1.1所示的一棵二叉树,其后序遍历的结果为DEBCA
实现非递归方式的后序遍历,有两种。
第一种:
使用两个栈来进行实现。注意到,后序遍历可以看做以下遍历过程的逆过程:先遍历某个节点,然后遍历其右孩子节点,再遍历其左孩子节点,该过程的逆过程,即为后序遍历的遍历过程。如图:

为此,我们可以按照如下的算法得到二叉树的后序遍历结果:
- 初始化两个栈,一个用于保存中间遍历过程称为栈s,一个用于保存最终的结果称为栈output
- push根节点到第一个栈s中
- 从第一个栈s中pop出一节点,并将其push到第二个栈output中
- 将第一个栈s中pop出的节点的孩子节点,按左孩子,右孩子的顺序push到第一个栈s中
- 重复步骤3和4直到栈s为空
- 栈s为空时,所有节点都已push到栈output中,且按后序遍历顺序存放,依次将栈output的节点pop出并进行访问,即为二叉树后序遍历的结果
以图1.1为例,其过程如下:

示例代码如下:
/**
* 用于实现二叉树的非递归方式的后序遍历,方式1
* @param root 二叉树的根节点
*/
public void PostRootTraverse(BinaryTreeNode root)
{
BinaryTreeNode T=root;
Stack s=new Stack();
Stack output=new Stack();
s.push(root);
//用于执行压入栈的过程
while(!s.isEmpty())
{
T=s.pop();
output.push(T);
if(T.leftChild!=null)
s.push(T.leftChild);
if(T.rightChild!=null)
s.push(T.rightChild);
}
//用于执行对遍历结果的每个节点的访问过程
while(!output.isEmpty())
{
System.out.println(((BinaryTreeNode)output.pop()).data);
}
}
第二种:
使用一个栈来进行实现,在搜索遍历的过程中,从二叉树的根节点出发,沿着该节点的左子树向下搜索,在搜索的过程中每遇到一个节点判断该节点是否是第一次经过,若是,则不立即访问,而是将该节点入栈保存,遍历该节点的左子树。当左子树遍历完毕后再返回该节点,这时还不能立即访问该节点,而是应当继续进入该节点的右子树进行遍历,当左右子树均遍历完毕后,才能从栈顶弹出该节点并访问它。由于在决定栈顶节点是否能访问时,需要知道该节点的右子树是否已经被遍历完毕。因此,为解决这个问题,在算法中还应当引入一个布尔型的访问标志变量flag和一个节点指针p。其中flag用来标志当前栈顶节点是否被访问过,当值为true的时候,表示栈顶节点已被访问过,当值为false的时候,表示当前栈顶节点未被访问过,指针p指向当前遍历过程中最后一个访问的节点。若当前栈顶节点的右孩子节点是空,或者就是p指向的节点,则表明当前节点的右子树已遍历完毕,此时就可以访问当前栈顶节点。其操作的实现过程描述如下:
- 创建一个栈对象,根节点进栈,p赋初始化值为null
- 若栈非空,则栈顶节点的非空左孩子相继进栈
- 若栈非空,查看栈顶节点,若栈顶节点的右孩子为空,或者与p相等,则将栈顶节点弹出栈并访问它,同时使p指向该节点,并置flag为true,否则,将栈顶节点的右孩子压入栈,并置flag的值为false
- 若flag值为true,则重复执行步骤3。否则,重复执行步骤2和3,直到栈为空为止。
示例代码如下:
package queueandstack;
import java.util.Stack;
/**
* 用于演示二叉树的遍历的三种方式的代码
* @author 学徒
*
*/
public class AroundTree
{
/**
* 用于实现二叉树的非递归方式的后序遍历,方式2
* @param root 二叉树的根节点
*/
public void PostRootTraverse(BinaryTreeNode root)
{
BinaryTreeNode T=root;
if(T!=null)
{
Stack s=new Stack();
s.push(T);//根节点进栈
boolean flag;//访问标记
BinaryTreeNode p=null;//p指向刚被访问的节点
while(!s.isEmpty())
{
while(s.peek()!=null)//将栈顶节点的左孩子相继入栈
{
s.push(((BinaryTreeNode)s.peek()).leftChild);
}
s.pop();//空节点退栈
}
while(!s.isEmpty())
{
T=(BinaryTreeNode)s.peek();//查看栈顶元素
if(T.rightChild==null||T.rightChild==p)
{
System.out.println(T.data);//访问节点
s.pop();//移除栈顶元素
p=T;//p指向刚被访问过的节点
flag=true;//设置访问标记
}
else
{
s.push(T.rightChild);//右孩子节点入栈
flag=false;//设置未被访问标记
}
if(!flag)
break;
}
}
}
}
先序遍历:
所谓的先序遍历,是指对一棵二叉树按照根节点,左子树,右子树的顺序递归的在一棵二叉树中的左右子树中访问相关节点的方式。如图1.1所示的一棵二叉树,其先序遍历的结果是ABDEC
实现非递归方式的先序遍历,有两种。
第一种:
注意到,先序遍历的过程为“根左右”,后序遍历的过程为“左右根”,其后序遍历的逆过程为“根右左”,其后序遍历过程可以采用两个栈形式来进行实现,实现的过程中,第一个栈存放当前节点的左孩子节点和右孩子节点,而其得到的出栈结果为“根右左”,为此,我们只需要将入第一个栈的节点顺序调换以下(即先入右节点,再入左节点)即可得到“根左右”的出栈顺序,其具体步骤如下描述:
- 初始化一个栈和一个队列
- push根节点入栈
- 从该栈中pop出其一节点,并将该节点加入到队列中
- 将该栈中pop出的节点的孩子节点,按照右孩子和左孩子的顺序push到该栈中
- 重复步骤2和步骤3直至栈为空
- 完成之后,所有的节点都push到该队列中,且按先序遍历的方式顺序存放,直接pop出队列中的节点,即为二叉树的先序遍历结果。
以图1.1为例,其过程如下:

示意代码如下:
/**
* 用于实现二叉树的非递归方式的先序遍历,方式1
* @param root 二叉树的根节点
*/
public void PreRootTraverse(BinaryTreeNode root)
{
BinaryTreeNode T=root;
Stack s=new Stack();
Queue q=new LinkedList();
s.push(root);
while(!s.isEmpty())
{
T=s.pop();
q.push(T);
if(T.rightChild!=null)
s.push(T.rightChild);
if(T.leftChild!=null)
s.push(T.leftChild);
}
//用于得到先序遍历的结果
while(!q.isEmpty())
{
System.out.println(((BinaryTreeNode)q.pop()).data);
}
}
第二种:
借助一个栈来记载当前被访问节点的右孩子节点,以便在遍历完一个节点的左子树后,能够顺利的进入这个节点的右子树进行遍历。从二叉树的根节点出发,沿着该节点的左子树向下搜索,在搜索过程中遇到一个节点就先访问该节点,并将该节点的非空右孩子节点压入栈中,当左子树访问完成之后,从栈顶弹出一个节点的右孩子节点,然后用上述同样的方法去遍历该节点的右子树,以此类推,直至二叉树中的所有节点都被访问了为止。其过程描述如下:
- 创建一个栈对象,根节点入栈
- 当栈为非空时,将栈顶节点弹出栈内并访问该节点
- 对当前访问节点的非空左孩子节点相继依次访问,并将当前访问节点的非空右孩子节点压入栈内
- 重复执行步骤2和3,直到栈为空为止
示例代码如下:
package queueandstack;
import java.util.Stack;
/**
* 用于演示二叉树的遍历的三种方式的代码
* @author 学徒
*
*/
public class AroundTree
{
/**
* 用于实现二叉树的非递归方式的先序遍历,方式2
* @param root 二叉树的根节点
*/
public void PreRootTraverse(BinaryTreeNode root)
{
BinaryTreeNode T=root;
if(T!=null)
{
Stack s=new Stack();
s.push(T);//根节点入栈
while(!s.isEmpty())
{
T=(BinaryTreeNode)s.pop();//移除栈顶节点,并返回其值
System.out.println(T.data);//访问节点
while(T!=null)
{
if(T.leftChild!=null)//访问左孩子节点
System.out.println(T.leftChild.data);//访问节点
if(T.rightChild!=null)//右孩子非空入栈
s.push(T.rightChild);
T=T.leftChild;
}
}
}
}
}
中序遍历:
所谓的中序遍历是指对一棵二叉树按照左子树,根节点,右子树的顺序递归的在一棵二叉树的左右子树中访问相关节点的方式。如图1.1所示的一棵二叉树,其中序遍历的结果为DBEAC。
进行中序遍历可以借助一个栈来记载遍历过程中所经历的而未被访问过的所有节点,以便遍历完一个节点左子树后能顺利返回到它的父节点。实现中根遍历操作的非递归算法的主要思想是:从二叉树的根节点出发,沿着该节点的左子树向下搜索,在搜索过程中将所遇到的每一个节点依次压栈,直到二叉树中最坐下的的节点压栈为止,然后从栈中弹出栈顶节点并对其进行访问,访问完后再进入该节点的右子树并用上述的同样的方法去遍历该节点的右子树,以此类推,直到二叉树中的所有节点都被访问为止。
示例代码如下:
package queueandstack;
import java.util.Stack;
/**
* 用于演示二叉树的遍历的三种方式的代码
* @author 学徒
*
*/
public class AroundTree
{
/**
* 用于实现二叉树的非递归方式的中序遍历
* @param root 二叉树的根节点
*/
public void PreRootTraverse(BinaryTreeNode root)
{
BinaryTreeNode T=root;
if(T!=null)
{
Stack s=new Stack();
s.push(T);
while(!s.isEmpty())
{
while(s.peek()!=null)//将栈顶节点的左孩子节点相继入栈
s.push(((BinaryTreeNode)s.peek()).leftChild);
s.pop();//空节点退栈
//在遍历节点时,将该节点对应的右孩子节点压入栈中
if(!s.isEmpty())
{
T=(BinaryTreeNode)s.pop();//得到未访问的栈顶节点,并返回其值
System.out.println(T.data);
s.push(T.rightChild);//节点的右孩子入栈
}
}
}
}
}
K:二叉树的非递归遍历的更多相关文章
- ZT 二叉树的非递归遍历
ZT 二叉树的非递归遍历 二叉树的非递归遍历 二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的.对于二叉树,有前序.中序以及后序三种遍历方法.因为树的定义本身就 是递归定 ...
- [Alg] 二叉树的非递归遍历
1. 非递归遍历二叉树算法 (使用stack) 以非递归方式对二叉树进行遍历的算法需要借助一个栈来存放访问过得节点. (1) 前序遍历 从整棵树的根节点开始,对于任意节点V,访问节点V并将节点V入栈, ...
- 二叉树的非递归遍历C++实现
#include<iostream> #include<stdlib.h> #define maxsize 100 using namespace std; typedef s ...
- C++编程练习(17)----“二叉树非递归遍历的实现“
二叉树的非递归遍历 最近看书上说道要掌握二叉树遍历的6种编写方式,之前只用递归方式编写过,这次就用非递归方式编写试一试. C++编程练习(8)----“二叉树的建立以及二叉树的三种遍历方式“(前序遍历 ...
- Java实现二叉树的创建、递归/非递归遍历
近期复习数据结构中的二叉树的相关问题,在这里整理一下 这里包含: 1.二叉树的先序创建 2.二叉树的递归先序遍历 3.二叉树的非递归先序遍历 4.二叉树的递归中序遍历 5.二叉树的非递归中序遍历 6. ...
- 数据结构二叉树的递归与非递归遍历之java,javascript,php实现可编译(1)java
前一段时间,学习数据结构的各种算法,概念不难理解,只是被C++的指针给弄的犯糊涂,于是用java,web,javascript,分别去实现数据结构的各种算法. 二叉树的遍历,本分享只是以二叉树中的先序 ...
- 二叉树3种递归和非递归遍历(Java)
import java.util.Stack; //二叉树3种递归和非递归遍历(Java) public class Traverse { /******************一二进制树的定义*** ...
- c/c++二叉树的创建与遍历(非递归遍历左右中,破坏树结构)
二叉树的创建与遍历(非递归遍历左右中,破坏树结构) 创建 二叉树的递归3种遍历方式: 1,先中心,再左树,再右树 2,先左树,再中心,再右树 3,先左树,再右树,再中心 二叉树的非递归4种遍历方式: ...
- JAVA递归、非递归遍历二叉树(转)
原文链接: JAVA递归.非递归遍历二叉树 import java.util.Stack; import java.util.HashMap; public class BinTree { priva ...
随机推荐
- android应用集成facebook登录
之前都是做国内的应用开发,没有用过国外的三方登录,比如谷歌登录.facebok登录,最近参与了一个海外的支付相关的项目,调研了一下谷歌登录和facebook登录,其实调研之后觉得也是很简单的,尤其 ...
- 使用Xamarin开发手机聊天程序 -- 基础篇(大量图文讲解 step by step,附源码下载)
如果是.NET开发人员,想学习手机应用开发(Android和iOS),Xamarin 无疑是最好的选择,编写一次,即可发布到Android和iOS平台,真是利器中的利器啊!而且,Xamarin已经被微 ...
- centos yum install redis
linux下yum安装redis以及使用 1.yum install redis --查看是否有redis yum 源 [root@localhost ~]# yum install r ...
- SQL基础教程读书笔记-2
4 数据更新 4.1数据的插入 1.清单{①列清单 ②值清单列清单和值清单的数量必须保持一致.原则上,执行一次INSERT语句会插入一行数据对表进行全列INSERT时,可以省略表名后的列清单2.插入默 ...
- 关于子线程更新UI
大家都了解的子线程不能更新UI,所以普通青年比方我,遇到耗时操作用到线程时.不得不立刻想到了用handler传递来解决UI更细的问题. 普通青年的做法: 方案:使用Thread+handler方式,h ...
- Spring Cache简单介绍和使用
Spring Cache 缓存是实际工作中非经常常使用的一种提高性能的方法, 我们会在很多场景下来使用缓存. 本文通过一个简单的样例进行展开,通过对照我们原来的自己定义缓存和 spring 的基于凝视 ...
- java文本编辑器v2.0 图形用户界面
package 文本编辑器; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; impor ...
- TCP服务端开发为例--web开发不同url请求走不同control方法
拿java的web开发为例子,相信有很多小伙伴是做j2EE开发的,htpp请求,json数据传输都是工作中经常用的,查询请求,添加请求,修改请求前端配个url,例如https://localhost/ ...
- jquery查找元素
一:查找元素 * 所有元素 element 该名称的所有元素(p,input) #id 拥有指定id属性的元素 .class 拥有所有指定class属性的元素 selector1,selector2 ...
- Asp.net MVC 填充word并下载
使用Aspose.word填充内容并下载(免费版Aspose) 填充固定模式的Word文档,需要先制作Word模板: Aspose.Words主要通过Words里域(Fields)来控制内容. ...