相关介绍:

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

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

后序遍历:

 所谓的后序遍历是指对一棵二叉树按照左子树,右子树,根节点的顺序递归的在一棵二叉树的左右子树中访问相关节点的方式。如图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. 浅析文本挖掘(jieba模块的应用)

    一,文本挖掘 1.1,什么是文本挖掘 文本挖掘是指从大量文本数据中抽取事先未知的,可理解的,最终可用的知识的过程,同时运用这些知识更好的组织信息以便将来参考 1.2,文本挖掘基本流程 收集数据 数据集 ...

  2. NOIP2014无线网络发射器选址改编1

    问题描述 随着智能手机的日益普及,人们对无线网的需求日益增大.某城市决定对城市内的公共场所覆盖无线网. 假设该城市的布局为由严格平行的129条东西向街道和129条南北向街道所形成的网格状,并且相邻的平 ...

  3. Spring 面试

    1.什么是Spring框架?Spring框架有哪些主要模块? spring框架是一个为Java应用程序的开发提供了综合.广泛的基础性支持的Java平台.Spring帮助开发者解决了开发中基础性的问题, ...

  4. 【C语言】统计数组中出现次数超过一半的数字

    //统计数组中出现次数超过一半的数字 #include <stdio.h> int Find(int *arr, int len) { int num = 0; //当前数字 int ti ...

  5. HDU 3569 Imaginary Date 简单期望

    推一下公式.就会发现是这个.. 由于设结果有x种方案.则每一个数字出现的概率都均等,然后和就是x*m 每种方案的概率是1/x 每一个数出现的概率都是1/n 所以每一个方案的和就是 sum/n *m # ...

  6. linux shell 推断文件或目录是否真的存在

    #推断文件或目录是否存在 filepath=/data/test.txt folderpath=/data/qtech #推断文件是否存在 if [ -f "$file" ] th ...

  7. xml文档读取-SAX

    由于dom采用的是将xml文档加载入内存进行处理的方式,如果xml文档较大,则会导致加载时间过长,效率降低的情况,因此,sun公司在JAXP中又添加了对SAX的支持: SAX,全称Simple API ...

  8. 【Sqlserver系列】【转载】事物与锁

    1   概述 本篇文章简要对事物与锁的分析比较详细,因此就转载了. 2   具体内容 并发可以定义为多个进程同时访问或修改共享数据的能力.处于活动状态而互不干涉的并发用户进程的数量越多,数据库系统的并 ...

  9. 项目实战12.2—企业级监控工具应用实战-zabbix操作进阶

    无监控,不运维.好了,废话不多说,下面都是干货. 流量党勿入,图片太多!!! 项目实战系列,总架构图 http://www.cnblogs.com/along21/p/8000812.html 一.U ...

  10. Effective Java 第三版——7. 消除过期的对象引用

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...