相关介绍:

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

运用下图对二叉树的三种遍历方式进行介绍:

后序遍历:

 所谓的后序遍历是指对一棵二叉树按照左子树,右子树,根节点的顺序递归的在一棵二叉树的左右子树中访问相关节点的方式。如图1.1所示的一棵二叉树,其后序遍历的结果为DEBCA

 实现非递归方式的后序遍历,有两种。

第一种:

 使用两个栈来进行实现。注意到,后序遍历可以看做以下遍历过程的逆过程:先遍历某个节点,然后遍历其右孩子节点,再遍历其左孩子节点,该过程的逆过程,即为后序遍历的遍历过程。如图:

为此,我们可以按照如下的算法得到二叉树的后序遍历结果:

  1. 初始化两个栈,一个用于保存中间遍历过程称为栈s,一个用于保存最终的结果称为栈output
  2. push根节点到第一个栈s中
  3. 从第一个栈s中pop出一节点,并将其push到第二个栈output中
  4. 将第一个栈s中pop出的节点的孩子节点,按左孩子,右孩子的顺序push到第一个栈s中
  5. 重复步骤3和4直到栈s为空
  6. 栈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指向的节点,则表明当前节点的右子树已遍历完毕,此时就可以访问当前栈顶节点。其操作的实现过程描述如下:

  1. 创建一个栈对象,根节点进栈,p赋初始化值为null
  2. 若栈非空,则栈顶节点的非空左孩子相继进栈
  3. 若栈非空,查看栈顶节点,若栈顶节点的右孩子为空,或者与p相等,则将栈顶节点弹出栈并访问它,同时使p指向该节点,并置flag为true,否则,将栈顶节点的右孩子压入栈,并置flag的值为false
  4. 若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

 实现非递归方式的先序遍历,有两种。

第一种:

 注意到,先序遍历的过程为“根左右”,后序遍历的过程为“左右根”,其后序遍历的逆过程为“根右左”,其后序遍历过程可以采用两个栈形式来进行实现,实现的过程中,第一个栈存放当前节点的左孩子节点和右孩子节点,而其得到的出栈结果为“根右左”,为此,我们只需要将入第一个栈的节点顺序调换以下(即先入右节点,再入左节点)即可得到“根左右”的出栈顺序,其具体步骤如下描述:

  1. 初始化一个栈和一个队列
  2. push根节点入栈
  3. 从该栈中pop出其一节点,并将该节点加入到队列中
  4. 将该栈中pop出的节点的孩子节点,按照右孩子和左孩子的顺序push到该栈中
  5. 重复步骤2和步骤3直至栈为空
  6. 完成之后,所有的节点都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);
}
}

第二种:

 借助一个栈来记载当前被访问节点的右孩子节点,以便在遍历完一个节点的左子树后,能够顺利的进入这个节点的右子树进行遍历。从二叉树的根节点出发,沿着该节点的左子树向下搜索,在搜索过程中遇到一个节点就先访问该节点,并将该节点的非空右孩子节点压入栈中,当左子树访问完成之后,从栈顶弹出一个节点的右孩子节点,然后用上述同样的方法去遍历该节点的右子树,以此类推,直至二叉树中的所有节点都被访问了为止。其过程描述如下:

  1. 创建一个栈对象,根节点入栈
  2. 当栈为非空时,将栈顶节点弹出栈内并访问该节点
  3. 对当前访问节点的非空左孩子节点相继依次访问,并将当前访问节点的非空右孩子节点压入栈内
  4. 重复执行步骤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:二叉树的非递归遍历的更多相关文章

  1. ZT 二叉树的非递归遍历

    ZT 二叉树的非递归遍历 二叉树的非递归遍历 二叉树是一种非常重要的数据结构,很多其它数据结构都是基于二叉树的基础演变而来的.对于二叉树,有前序.中序以及后序三种遍历方法.因为树的定义本身就 是递归定 ...

  2. [Alg] 二叉树的非递归遍历

    1. 非递归遍历二叉树算法 (使用stack) 以非递归方式对二叉树进行遍历的算法需要借助一个栈来存放访问过得节点. (1) 前序遍历 从整棵树的根节点开始,对于任意节点V,访问节点V并将节点V入栈, ...

  3. 二叉树的非递归遍历C++实现

    #include<iostream> #include<stdlib.h> #define maxsize 100 using namespace std; typedef s ...

  4. C++编程练习(17)----“二叉树非递归遍历的实现“

    二叉树的非递归遍历 最近看书上说道要掌握二叉树遍历的6种编写方式,之前只用递归方式编写过,这次就用非递归方式编写试一试. C++编程练习(8)----“二叉树的建立以及二叉树的三种遍历方式“(前序遍历 ...

  5. Java实现二叉树的创建、递归/非递归遍历

    近期复习数据结构中的二叉树的相关问题,在这里整理一下 这里包含: 1.二叉树的先序创建 2.二叉树的递归先序遍历 3.二叉树的非递归先序遍历 4.二叉树的递归中序遍历 5.二叉树的非递归中序遍历 6. ...

  6. 数据结构二叉树的递归与非递归遍历之java,javascript,php实现可编译(1)java

    前一段时间,学习数据结构的各种算法,概念不难理解,只是被C++的指针给弄的犯糊涂,于是用java,web,javascript,分别去实现数据结构的各种算法. 二叉树的遍历,本分享只是以二叉树中的先序 ...

  7. 二叉树3种递归和非递归遍历(Java)

    import java.util.Stack; //二叉树3种递归和非递归遍历(Java) public class Traverse { /******************一二进制树的定义*** ...

  8. c/c++二叉树的创建与遍历(非递归遍历左右中,破坏树结构)

    二叉树的创建与遍历(非递归遍历左右中,破坏树结构) 创建 二叉树的递归3种遍历方式: 1,先中心,再左树,再右树 2,先左树,再中心,再右树 3,先左树,再右树,再中心 二叉树的非递归4种遍历方式: ...

  9. JAVA递归、非递归遍历二叉树(转)

    原文链接: JAVA递归.非递归遍历二叉树 import java.util.Stack; import java.util.HashMap; public class BinTree { priva ...

随机推荐

  1. PDFBox 打印带背景的文件速度慢

    打印慢的原因 java的RasterPrinterJob会执行很多次printPage方法 他应该是按块填充的, 如果页面元素非常复杂, 那么printPage方法可能会执行十几次. 而如果你用了如下 ...

  2. 网络协议之ipv6

    1. 地址分类 比較重要的主要有以下几种: 本地链路地址:用于链路之间相互通信 本地网站地址:用于子网内互相通信,类似于ipv4中的私有地址 全球单播地址:类似于ipv4中的公网地址 组播地址 2. ...

  3. Android 当打开“开发人员模式”中的“不保留活动”后,程序应当怎么保持正常执行

    Android 当打开"开发人员模式"中的"不保留活动"后,程序应当怎么保持正常执行咧. .? 在这几天,我一直在纠结这个问题.从发现,程序出现这个问题,是由于 ...

  4. opencl 和Renderscript总结

    在android上要开发opencl.手机端要有libopencl.so文件(也就是opencl驱动).可是如今android手机端非常少有这个文件.原因是尽管AMD.Intel.NVIDIA.苹果等 ...

  5. Linux 高速操作IOport

    在嵌入式设备中对GPIO的操作是最主要的操作. 一般的做法是写一个单独驱动程序,网上大多数的样例都是这种.事实上linux以下有一个通用的GPIO操作接口.那就是我要介绍的 "/sys/cl ...

  6. 十二、 Spring Boot 静态资源处理

    spring Boot 默认为我们提供了静态资源处理,使用 WebMvcAutoConfiguration 中的配置各种属性. 建议大家使用Spring Boot的默认配置方式,如果需要特殊处理的再通 ...

  7. 百度地图点集文档使用python的re模块处理成json的相关写法

    这个实在不好起名字.写这个还不是因为被渣度坑的不要不要的.为什么说他坑呢.参考一下这两个截图的txt文档: 文档资源下载地址:  http://lbsyun.baidu.com/index.php?t ...

  8. 【JavaScript】 JS面向对象的模式与实践 (重点整治原型这个熊孩子 (/= _ =)/~┴┴ )

    参考书籍 <JavaScript高级语言程序设计>—— Nicholas C.Zakas <你不知道的JavaScript>  —— KYLE SIMPSON   在JS的面向 ...

  9. Python datatime 格式转换,插入MySQL数据库

    Python datatime 格式转换,插入MySQL数据库 zoerywzhou@163.com http://www.cnblogs.com/swje/ 作者:Zhouwan 2017-11-2 ...

  10. spring -boot s-tarter 详解

    Starter POMs是可以包含到应用中的一个方便的依赖关系描述符集合.你可以获取所有Spring及相关技术的一站式服务,而不需要翻阅示例代码,拷贝粘贴大量的依赖描述符.例如,如果你想使用Sprin ...