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 ...
随机推荐
- linux上kafka模拟客户端发送、接受消息
producer 消息的生成者,即发布消息 consumer 消息的消费者,即订阅消息 broker Kafka以集群的方式运行,可以由一个或多个服务组成,服务即broker zook ...
- easyUI带复选框的组合树
代码: <input id="depts"><script type="text/javascript">$(document).rea ...
- less新手入门(二) Mixin 混合、带参数的Mixin
四.mixin 混合 "mix - in"属性来自现有的样式!! 你可以在类选择器和id选择器中使用mixin, .a,#b{ color: rebeccapurple; } . ...
- POJ 2195Going Home(网络流之最小费用流)
题目地址:id=2195">POJ2195 本人职业生涯费用流第一发!!快邀请赛了.决定还是多学点东西.起码碰到简单的网络流要A掉.以后最大流费用流最小割就一块刷. 曾经费用流在我心目 ...
- Android开发中有用工具之--Log工具类
在开发的过程中.我们常常会使用Log来输出日志,帮助我们来调试程序 可是有时候并不能全然满足我们的须要 ,比方我想知道这个日志信息是来自于哪一个包 哪一个类 所以我们封装一个这个Log类.方便我们的使 ...
- bzoj1513【POI2006】Tet-Tetris 3D
1513: [POI2006]Tet-Tetris 3D Time Limit: 30 Sec Memory Limit: 162 MB Submit: 733 Solved: 245 [Subm ...
- 虚拟数据库_json_ajax
html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3 ...
- Python3简单的输入输出及内置函数查看
工作之余和女朋友一起学Python3,代码都是她敲的,有点辣眼睛,仅做参考. 1.题目:输入"姓名",输出"你好,姓名" 有关安装和打开Python shell ...
- String、StringBuilder和StringBuffer类
*/ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...
- Redis命令与配置
命令 开启服务端 redis-server.exe redis.conf 客户端连接 redis-cli.exe -h 127.0.0.1 -p 6379 1.连接操作相关的命令 quit:关闭连接( ...