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 ...
随机推荐
- UWP 图片缩放
给Image外面包裹一个ScrollViewer,你会回来感激我的. 哦,对了,PC上需要按住Ctrl键,滑动鼠标滑轮即可:手机上双指就可以缩放. <ScrollViewer ZoomMode= ...
- BFS+数据处理 Under the Trees UVa
题意:将多叉树转化为括号表示法,每个非叶结点的正下方都有一个'|'然后下方是一排'-'和字符,恰好覆盖所有子结点的正上方,单独的一行'#'为数据的结束标志 解题思路:用gets将字符数组输入,本题不用 ...
- tomcat线程初探
博主:handsomecui,希望路过的各位大佬留下你们宝贵的意见,在这里祝大家冬至快乐. 缘由: 初探缘由,在业务层想要通过(当前线程的栈)来获取到控制层的类名,然后打日志,可是发现并不能通过当前线 ...
- 在centos7中手动编译greenplum
一.编译环境 Linux version 3.10.0-327.el7.x86_64 (builder@kbuilder.dev.centos.org) (gcc version 4.8.3 2014 ...
- 命令行界面下使用emca安装配置Oracle Database Control实战
作为命令行忠有用户,server端软件的运维都倾向于使用命令或 脚本完毕,非常讨厌资源占用非常大的GUI.Oracle数据库作为重要的server端软件.其安装运维自然也全然支持纯命令行方式.虽然同一 ...
- BZOJ 1211 HNOI2004 树的计数 Prufer序列
题目大意:给定一棵树中全部点的度数,求有多少种可能的树 Prufer序列.详细參考[HNOI2008]明明的烦恼 直接乘会爆long long,所以先把每一个数分解质因数.把质因数的次数相加相减.然后 ...
- 前端笔记----类型转换display
display属性用来在行内元素,块元素,行内块元素之间进行转化. 常用的属性有: 1.none :元素隐藏且不占位置,相当于不存在,一般用在动态展示效果:2.block :元素以块元素显示,有些行内 ...
- centos6.5安装git
1.git源码地址:http://codemonkey.org.uk/projects/git-snapshots/git/
- Python 项目实践一(外星人入侵小游戏)第五篇
接着上节的继续学习,在本章中,我们将结束游戏<外星人入侵>的开发.我们将添加一个Play按钮,用于根据需要启动游戏以及在游戏结束后重启游戏.我们还将修改这个游戏,使其在玩家的等级提高时加快 ...
- Linux chgrp
在学习 兄弟连 linux教学视频 的时候,我将所学的 linux 命令记录在我的博客中,方便自己查阅. 权限管理命令: chgrp 基础的命令 命令名称:chgrp 命令英文原意:change fi ...