二叉树的遍历(包括递归和非递归方法)
二叉树的遍历
遍历二叉树,就是按一定的某条搜索路径走遍二叉树的所有结点,使每一个结点都被访问一次,而且只被访问一次。由于二叉树是非线性结构,每个结点都有可能有两棵子树,因此,树的遍历实质上是将二叉树的各个结点转换成为一个线性序列来表示。
递归遍历
由二叉树的递归定义可知,遍历一棵二叉树便要决定对根节点N,左子树L和右子树R的访问顺序,所以按照先遍历左子树后遍历右子树的原则,常见的遍历次序有先序(NLR),中序(LNR)和后序(LRN)三种遍历算法;
【注】“序”指的是遍历过程中根节点何时被访问。
下面分别介绍一下这三种遍历的操作过程
1.先序遍历
若二叉树为空,则什么也不做,否则依次执行如下操作:
(1)访问根结点;
(2)先序遍历左子树;
(3)先序遍历右子树;
对应的递归算法如下:
void PreOrder(BiTree T){if(T != NULL){visit(T); //访问根节点PreOrder(T->lchild); //递归遍历左子树PreOrder(T->rchild); //递归遍历右子树}
}
1.中序遍历
若二叉树为空,则什么也不做,否则依次执行如下操作:
(1) 中序遍历左子树;
(2)访问根结点;
(3) 中序遍历右子树;
对应的递归算法如下:
void InOrder(BiTree T){if(T != NULL){InOrder(T->lchild); //递归遍历左子树visit(T); //访问根节点InOrder(T->rchild); //递归遍历右子树}
}
1.后序遍历
若二叉树为空,则什么也不做,否则依次执行如下操作:
(1)后序遍历左子树;
(2)后序遍历右子树;
(3)访问根结点;
对应的递归算法如下:
void PostOrder(BiTree T){if(T != NULL){PostOrder(T->lchild); //递归遍历左子树PostOrder(T->rchild); //递归遍历右子树visit(T); //访问根节点}
}
下面根据上面三种遍历算法看一个例子:
箭头的虚线表示3种算法的递归执行过程,其中向下的箭头表示更深一点的递归调用,向上的箭头表示从递归调用退出返回;虚线旁的三角形,圆形,方形内的字符分别表示在先序,中序和后序遍历过程种访问结点输出的信息
根据上面的算法可以知道,这三种遍历访问图中结点的顺序为:
先序遍历:A B D E C;
中序遍历:D B E A C;
后序遍历:D E B C A;
从上面我们可以看出这三种遍历算法中,递归遍历左,右子树的顺序都是一样的,只是访问根节点的顺序不同。每个结点都访问了且仅访问一次,所以其时间复杂度为O(n);
非递归遍历
递归方法是相对简单的,这是由二叉树的结构决定的;但非递归的思路也是比较简单的只是算法实现相对麻烦很多;那么根据递归变非递归的方法,我们可以借用栈来解决这个问题。
1.先序遍历
我们将树的每个节点压入栈中,由于是先序遍历,首先压入的是根节点,然后弹出(弹出节点时打印信息,且一个循环弹出一个节点),接着是压入右子树节点,最后压入左子树节点。(堆栈是先进先出的)
void PreOrderTraverse(BinTree b)
{InitStack(S); //初始化创建栈BinTree p=b; //p为工作指针while(p||!isEmpty(s)){while(p) //到最左下的孩子{printf(" %c ",p->date); //先序先遍历结点Push(S,p); //入栈p=p->lchild;}if(!isEmpty(s)) //在栈不为空的情况下,左孩子为空,弹出该结点,遍历右孩子{p=Pop(s);p=p->rchild;}}
}
中序遍历
中序时,我们首先去遍历二叉树的左分支,并将节点压入栈中,只到找到最左边的叶节点,接着弹出(并打印节点),并看其有没右分支,如果没有,栈再弹出一个节点(根节点),看其有没有右分支。每次弹出,都要观察其是否有右分支,也就是说每个节点都遍历了两次!
void InOrderTraverse(BinTree b)
{InitStack(S); //初始化创建栈BinTree p=b; //p为工作指针while(p||!isEmpty(s)){while(p){Push(S,p); //中序现将结点进栈保存p=p->lchild;} //遍历到左下角尽头再出栈访问if(!isEmpty(s)) //在栈不为空的情况下,左孩子为空,弹出该结点,遍历右孩子{p=Pop(s);printf(" %c ",p->data);p=p->rchild; //遍历右孩子}}
}
后序遍历
后序遍历在意思上和前序遍历相近,而前序遍历的压栈顺序为:根、右、左。那么如果我们使用两个堆栈,第一个压栈顺序为:根、左、右,但是在(先序遍历时)弹出根节点时将根节点压入第二个堆栈,为什么这里压栈顺序要为左右呢?很简单,在第一个堆栈中最后压入右子树,那么右子树会最先压入第二个堆栈,相应的,当第二个堆栈弹出时,右子树会在左子树的后面弹出(先进后出)。注意:根节点是最先被压入第一个栈中的,同时也是最先被压入第二个栈中的!
void PostOrderTraverse(BinTree b)
{InitStack(S); //初始化创建栈BinTree p=b, r=NULL; //p为工作指针,辅助指针rwhile(p||!isEmpty(s)){if(p) //从根节点到最左下角的左子树都入栈{Push(S,p); //中序现将结点进栈保存p=p->lchild;}else{GetTop(S,p); //取栈顶,注意!不是出栈!if(p->rchild&&p->rchild!=r) //右子树还没有访问并且右子树不空,第一次栈顶{p=p->rchild; //进入右子树}else //右子树已经访问或为空,接下来出栈访问结点,第二次栈顶{p=Pop(s);printf(" %c ",p->data);r=p; //指向访问过的右子树结点p=NULL; //使p为空继续访问栈顶}}}
}
层序遍历
如下图所示为二叉树的层序遍历,即按照箭头所示的方向,一层一层的访问二叉树的结点。
要进行层序遍历,需要借助一个队列,先将二叉树的根节点入队列,然后出队,访问出队结点,若它有左子树,先将左子树根节点入队;若它有右子树,则右子树根节点入队。然后出队,访问队结点…如此循环反复,直至队列为空。
二叉树的层序遍历算法如下:
void LevelOrder(BiTree T){InitQueue(Q); //初始化辅助队列BiTree p;EnQueue(Q,T); //将根节点入队;while(!IsEmpty(Q)){ //队列不为空则循环DeQueue(Q,p) //对头结点出队visit(p); //访问出队结点if(p->lchild != NULL)EnQueue(Q,p->lchild); //左孩子不空,左子树根节点入队if(p->rchild != NULL)EnQueue(Q,p->rchild); //右孩子不空,右子树根节点入队 }
}
二叉树的遍历(包括递归和非递归方法)相关推荐
- 二叉树的遍历(递归与非递归实现)
二叉树的遍历(递归与非递归实现) 二叉树的实现(三叉链表的形式) public class XieChaoThreeLinkBinTree<E> {public static class ...
- 二叉树的遍历 (递归和非递归实现)
二叉树的遍历 (递归实现) 用C++实现二叉树的"先根遍历"存储. 用C++实现二叉树的"先根遍历"."中根遍历"."后根遍历&q ...
- 二叉树的遍历(递归,非递归,Morris)
二叉树的遍历 目录 递归遍历 非递归遍历 Morris遍历 1. 递归遍历 递归版遍历只要当前节点不为null,就可以三次回到当前节点. public static void preOrderRecu ...
- 二叉树的遍历(递归、非递归)
背景 二叉树是一种很基本的数据结构.很多地方能看到它的身影,比如大名鼎鼎的霍夫曼编码(好了,别问我再比如了,见识浅薄,真不知道更多了...)它的结构很简洁.巧妙. 本文讨论二叉树的常见遍历方式的代码实 ...
- 二叉树的遍历操作-递归和非递归
二叉树的遍历 二叉树的遍历是很常见的一种问题, 往常我们可以通过递归来实现二叉树的先序,中序以及后序的遍历操作, 但是将递归转化成非递归是件值得去思考的一件事, 接下来是具体实现 具体实现 二叉树的定 ...
- 二叉树层序遍历递归与非递归_二叉树的遍历「递归、非递归」以及自己的感受
--------又来了一次二叉树的遍历,每次都有不一样的理解!真不知道那些大佬是如何想出这些牛逼的算法的,佩服至极!!!!大家多写,多想,对这些的理解真的会发生变化!!! -------- ----- ...
- 二叉树遍历的递归、非递归方法(前序、中序、后序,层序)——Java实现
1. 二叉树的前序遍历(深度优先遍历) 二叉树的节点定义 public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(in ...
- 数据结构(六)二叉树的遍历(递归非递归方法)
数据结构(六)二叉树的遍历(递归非递归方法) 一.递归方法 1.先序遍历 void PreOrder(BiTree T) {visit(T);PreOrder(T->LChild)PreOrde ...
- 二叉树创建及遍历算法(递归及非递归)(转)
//二叉树处理头文件 //包括二叉树的结构定义,二叉树的创建,遍历算法(递归及非递归), /* 作者:成晓旭 时间:2001年10月7日(18:49:38-20:00:00) 内容:完成二叉树创建,二 ...
最新文章
- 美团面试题:String s = new String(111)会创建几个对象?
- php比较3个数大小写,php在前n个字符的字符串比较(大小写敏感)的函数strncmp()...
- 自学python可以找到好的工作吗-学好python能找到好工作吗?
- c中获取python控制台输出_在真实的tim中用C捕获控制台python打印
- Python数据挖掘与分析常用库官方文档
- SQLi LABS Less 27 联合注入+报错注入+布尔盲注+时间盲注
- 缠中说禅_缠中说禅严格笔画法
- mysql数据库表无法显示_【MySQL8.0.18】IDEA 连接数据库无法显示数据表
- 数据结构与算法实验题 4.2 Who is the strongest
- centos7如何安装samba-client_Docker: 教程07 - ( 如何对 Docker 进行降级和升级)
- WWDC2019: SwiftUI实现你的第一个App
- 软件测试视频教程下载:APP测试类型和方法
- matlab波导色散,有效折射率法求矩形波导色散曲线(附Matlab程序)
- RSA 算法图解+数学证明
- 一步步学习微软InfoPath2010和SP2010--第十二章节--管理和监控InfoPath Form Services(IPFS)(3)--安装Fiddler并监控IPFS表单加载过程
- 飞行的小鸟(Flybird)C语言小游戏C++简单小程序超简单
- 2022年安徽省和湖北省供应链创新与应用示范城市和示范企业申报条件流程
- 程序猿头头(async与await的原理)
- 菜鸟渗透日记29---python渗透测试编程之信息收集1-主机发现
- 最新小程序反编译详细教程,亲测可用