(王道408考研数据结构)第五章树-第三节1:二叉树遍历(先序、中序和后序)
文章目录
- 一:二叉树遍历概述
- 二:二叉树深度优先遍历
- (1)先序遍历-根左右(NLR)
- (2)中序遍历-左根右(LNR)
- (3)后序遍历-左右根(LRN)
- 总结:三种遍历方式动图演示
- 三:二叉树的层序遍历
一:二叉树遍历概述
二叉树遍历(traversing binary tree):从根节点开始,按照某种次序依次访问二叉树中的所有结点,使得每个结点被访问一次且仅被访问一次
- 访问: 访问是一个抽象操作,是指具体你遍历到这个节点应该做什么?比如说最简单的打印,修改值等等
- 次序:二叉树的遍历有别于普通线性结构,因为树的结点之间不存在唯一的前驱和后继的关系,下一个被访问的结点面临着不同的选择。由于选择的方式不同,遍历的次序也就不同了
二:二叉树深度优先遍历
大部分人其实都知道一些二叉树的遍历的口诀,例如
- 先序遍历是“根左右”;
- 中序遍历是“左根右”;
- 后序遍历是“左右根“”;
通过这样的口诀,能够很快的写出树的各种遍历次序,但如果问到为什么是这样,很多人却无法说清楚。
其实呈现出不同遍历方式的根本原因在于二叉树的递归结构和访问时机的不同
如下图,这三种遍历方式本质是一样的,每个结点都会经历三次访问,二叉树默认递归时其实就是先序遍历,也就是先根节点,再左,后右。而在不同时机访问,就会造成不同的遍历结果
(1)先序遍历-根左右(NLR)
先序遍历:第一次遇到这个结点访问,第二次遇到或第三次遇到时跳过该结点直接访问下一个结点
- 也即若二叉树为空,则返回空,否则先访问根节点,然后先序遍历左子树,再先序遍历右子树
先序遍历算法:二叉树定义采用的递归形式,所以其遍历代码也可以采用递归,形式极其简单
void PreOrderTraverse(BTNode* root)
{if(root==NULL)return NULL;printf("%c",root->data);//结点访问操作,也可以是其他PreOrderTraverse(BTNode->lchild);//再先序遍历左子树PreOrderTraverse(BTNode->rchild);//最后先序遍历右子树}
接下来,我们用下面的树来说明上述代码是怎样执行的
1:首先调用了
PreOrderTraverse(A)
,由于根节点A不为空,所以执行了printf
,字母A被打印
2:接着调用了
PreOrderTraverse(A->lchild)
,由于根节点A的左孩子不为空,所以执行了printf
,字母B被打印
3:接着调用了
PreOrderTraverse(B->lchild)
,由于结点B的左孩子不为空,所以执行了printf
,字母D被打印
4:接着调用了
PreOrderTraverse(D->lchild)
,由于结点D的左孩子不为空,所以执行了printf
,字母H被打印
5:接着调用了
PreOrderTraverse(H->lchild)
,但此时结点H没有左孩子,所以被调函数传入root==NULL
,直接return NULL
;PreOrderTraverse(H->lchild)
于是直接结束,立马执行PreOrderTraverse(H->rchild)
,执行了printf
,字母K被打印
6:接着调用了
PreOrderTraverse(K->lchild)
,访问K结点左孩子,但没有左孩子,所以调用后直接返回了NULL
,然后调用PreOrderTraverse(K->rchild)
,但没有右孩子,所以调用后直接返回了NULL
。那么这就导致PreOrderTraverse(H->rchild)
函数的结束,而PreOrderTraverse(H->rchild)
的结束就导致了PreOrderTraverse(D->lchild)
,于是会执行PreOrderTraverse(D->rchild)
,但没有右孩子,从而导致了PreOrderTraverse(B->lchild)
函数的结束,于是继续执行PreOrderTraverse(B->rchild)
,执行了printf
,字母E被打印
7:对于结点E来说,它也没有左右孩子,所以访问完成之后
PreOrderTraverse(B->rchild)
结束,它的结束自然导致了PreOrderTraverse(A->lchild)
的结束,于是A的左子树访问完毕,现在开始右子树,因此PreOrderTraverse(A->rchild)
,执行了printf
,字母C被打印
8:后续访问过程不再赘述。遍历结果为A−B−D−H−K−E−C−F−I−G−JA-B-D-H-K-E-C-F-I-G-JA−B−D−H−K−E−C−F−I−G−J
(2)中序遍历-左根右(LNR)
中序遍历:第一次遇到结点跳过,第二次再遇到结点访问,第三次遇到跳过
- 也即若树为空,则返回空,否则从根节点开始(注意并不是先访问根节点),中序遍历根节点的左子树,然后是访问根节点,最后中序遍历右子树
中序遍历算法:二叉树定义采用的递归形式,所以其遍历代码也可以采用递归,形式极其简单
void InOrderTraverse(BTNode* root)
{if(root==NULL)return NULL;InOrderTraverse(BTNode->lchild);//中序遍历左子树printf("%c",root->data);//结点访问操作,也可以是其他InOrderTraverse(BTNode->rchild);//中序遍历右子树}
接下来,我们用下面的树来说明上述代码是怎样执行的
1:首先调用
InOrderTraverse(A)
,结点A不为空,于是再调用InOrderTraverse(A->lchild)
,对于B来说也不空,于是再调用InOrderTraverse(B->lchild)
,对于D来说也不空,于是再调用InOrderTraverse(D->lchild)
。对于结点H,当其调用InOrderTraverse(H->lchild)
时,由于其左孩子为空,因此会返回NULL
,然后执行了printf
,字母H被打印
2:然后调用
InOrderTraverse(H->rchild)
,访问H的右孩子K,然后执行InOrderTraverse(K->lchild)
,但其左孩子为空所以返回NULL
,然后执行了printf
,字母K被打印
3:然后
InOrderTraverse(K->rchild)
,但K没有右孩子,所以这直接导致InOrderTraverse(H->rchild)
的结束,接着导致了InOrderTraverse(D->lchild)
,然后执行了printf
,字母D被打印
4:然后调用
InOrderTraverse(D->rchild)
,但D没有右孩子所以返回NULL
,这直接导致了InOrderTraverse(B->lchild)
的结束,然后执行了printf
,字母B被打印
5:然后调用
InOrderTraverse(B->rchild)
,接着再调用InOrderTraverse(E->lchild)
,但E没有左孩子所以返回NULL
,然后执行了printf
,字母E被打印
6:然后调用
InOrderTraverse(E->rchild)
,但E没有右孩子所以返回NULL
,这就导致了InOrderTraverse(B->rchild)
的结束,继而导致了InOrderTraverse(A->lchild)
的结束,然后执行了printf
,字母A被打印
7:然后调用
InOrderTraverse(A->rchild)
,开始A的右子树的遍历过程8:后续访问过程不再赘述。遍历结果为H−K−D−B−E−A−I−F−C−G−JH-K-D-B-E-A-I-F-C-G-JH−K−D−B−E−A−I−F−C−G−J
(3)后序遍历-左右根(LRN)
后序遍历:前两次遇见结点不访问,最后一次遇见时访问
- 也即若树为空,则返回空。否则从左到右先叶子后结点的方式遍历访问左右子树,最后根节点
后序遍历算法:二叉树定义采用的递归形式,所以其遍历代码也可以采用递归,形式极其简单
void PostnOrderTraverse(BTNode* root)
{if(root==NULL)return NULL;PostOrderTraverse(BTNode->lchild);//后序遍历左子树PostOrderTraverse(BTNode->rchild);//后序遍历右子树printf("%c",root->data);//结点访问操作,也可以是其他}
后序遍历大家可以自己推导一下
- 结果为:K−H−D−E−B−I−F−J−G−C−AK-H-D-E-B-I-F-J-G-C-AK−H−D−E−B−I−F−J−G−C−A
总结:三种遍历方式动图演示
本人在初学数据结构时,对于三个地方理解的不是特别深入,其中有一个便是二叉树的递归,就如空中楼阁一般,感觉明白了,其实没有明白,这种感觉相信大家深有体会,的确不好受。不过在接触了很多和递归算法相关的题目及案例后,对于其理解确实有了一定的进步
所以我相信对于很多人来说,递归这一部分的理解也不是很到位(除大佬外),而天勤相较于王道来说,对于递归这一部分讲解确实很不错,人家把递归一层层的为我们剖析开来了。下面的图取自其视频课,质量不高(因为图片有大小限制,进行了很多次压缩),但是确实是一帧一帧的录制的,希望可以对大家的理解有所帮助
先序遍历
中序遍历
后序遍历
三:二叉树的层序遍历
层次遍历:需要借助队列完成。若树为空,则返回空,然后从上至下,从左至右依次访问结点
- 初始化一个辅助队列
- 根结点入队
- 若队列非空,则队头结点出队,访问该结点,然后将其左、右孩子(如果有)插入队尾
- 重复第三步,直至队列为空
具体过程如下图
代码如下
void LevelOrder(BTNode* root)
{LinkQueue Q;InitQueue(Q);BTNode* p;//辅助结点EnQueue(Q,root);//先将根节点入队列while(isEmpty(Q)){DeQueue(Q,p);//出队列,p拿到结点visit(p);if(p->lchild!=NULL)EnQueue(Q,p->lchild);if(p->rchild!=NULL)EnQueue(Q,p->rchild);}
}
(王道408考研数据结构)第五章树-第三节1:二叉树遍历(先序、中序和后序)相关推荐
- (王道408考研数据结构)第五章树-第三节4:树与二叉树的转换
文章目录 一:树.二叉树和森林的转换 (1)树转化为二叉树 (2)森林转化为二叉树 (3)二叉树转化为树 (4)二叉树转化为森林 二:树与森林的遍历 (1)树的遍历 (2)森林的遍历 一:树.二叉树和 ...
- (王道408考研数据结构)第五章树-第三节2:二叉树构造和重建
文章目录 一:根据遍历序列构造二叉树 (1)由前序遍历和中序遍历构造二叉树 (2)由后序遍历和中序遍历构造二叉树 (3)由层次遍历和中序遍历构造二叉树 二:重建二叉树 一:根据遍历序列构造二叉树 (1 ...
- (王道408考研数据结构)第五章树-第三节3:线索二叉树
相较于链表,二叉树的递归结构为其操作带来了一定的便利.如下二叉树的中序遍历结果为 D − G − B − E − A − F − C D-G-B-E-A-F-C D−
- (王道408考研数据结构)第五章树-第四节2:平衡二叉树(AVL)及其旋转
文章目录 一:AVL树基本概念 二:AVL树实现原理 (1)构建AVL树 (2)构建演示 (3)旋转方法 A:右单旋转调整(插入到较高左子树左侧) B:左单旋转调整(插入到较高右子树右侧) C:先左后 ...
- (王道408考研数据结构)第五章树-第四节3:哈夫曼树基本概念、构造和哈夫曼编码
文章目录 一:哈夫曼树基本概念 (1)相关术语 (2)哈夫曼树定义 二:哈夫曼树的构造 三:哈夫曼树特点 四:哈夫曼树典型应用-哈夫曼编码 在计算机中, 文件压缩是一项非常重要的技术,它除了可以减少文 ...
- (王道408考研数据结构)第五章树-第四节1:二叉树排序树(BST)及其操作
文章目录 一:二叉排序树基本概念 二:二叉排序树查找 二:二叉排序树插入/构建 四:二叉排序树删除 (1)如果左子树为空 (2)如果右子树为空 (3)如果左右子树都不为空树 代码 测试 一:二叉排序树 ...
- (王道408考研数据结构)第五章树-第一节:树的定义、基本用语和常考性质
文章目录 一:树基本概念 (1)树的定义 (2)结点分类 (3)结点关系(相关术语) 二:树的常考性质 一:树基本概念 (1)树的定义 树(Tree):这是一种非线性结构.是 n n n(
- (王道408考研数据结构)第五章树-第四节4:红黑树基本概念及操作
文章目录 一:红黑树基本概念 (1)什么是红黑树 (2)红黑树的性质 (3)为什么要给空结点(NIL)上色? (4)为什么最长路径一定不超过最短路径的2倍? (5)红黑树效率 二:红黑树的实现 (1) ...
- (王道408考研数据结构)第七章查找-第三节:B树(基本概念及其操作)
文章目录 一:B树的基本概念 (1)B树 (2)B树(假设 m m m阶)特点及效率 二:B树操作 (1)插入 (2)查找 (3)删除 一:B树的基本概念 (1)B树 B树(B-tree)
最新文章
- 新生儿信息管理系统升级说明
- .NET Core开发实战(第10课:环境变量配置提供程序)--学习笔记
- php中可以实现分支,PHP中的分支及循环语句
- python打印二进制内容_在python中打印出c类型的二进制表示
- 思考一下http.ListenAndServe + echo+gorm+xorm的可行性?
- canvas 绘制圆形进度条
- 今晚8点,Oracle ACE男神Roger详解Oracle中为什么没有double write?
- python编程设计_程序设计入门—Python
- omnigraffle 画曲线_OmniGraffle使用的10个小技巧
- java调用iec61850_IEC61850开发实战(三)
- 关于QTTabBar的使用
- 基于物品的协同过滤算法实现图书推荐系统
- 网络唤醒Wake On Lan
- IQOO手机便签有哪些实用的小功能?
- Kubernetes基础:使用rollout对Deployment进行控制
- #2991. kiki君的护盾 (shield)
- 11.03 CSS的基本使用
- 数电常见74系列器件及其功能总结(不要再混乱啦,值得收藏)
- QOpenGLWidget显示视频流数据
- DL之RNN:人工智能为你写周董歌词——基于TF利用RNN算法实现【机器为你作词】、训练测试过程全记录
热门文章
- php curl登陆邮箱,php利用CURL函数登入163邮箱并获取自己的通讯录
- mybatis没有导入sqlsessionfactory包_MyBatis——搭建第一个MyBatis
- java 界面输出控制台信息,java 怎么获取控制台的数据并且输出到GUI上
- python模拟登陆遇到重定向_python - scrapy模拟登陆知乎出现重定向无法登陆问题
- ThinkPHP框架在linux服务器nginx环境下跑出现溢出缓存问题解决方法
- 网易云课堂解析_使用SQL分析网易云课堂职场提升类课程
- mui 头部tab代码2
- thinkphp 分页出错 $page-render() 出错
- 单条MySQL最长_MySQL 单条记录长度最大65535
- videojs暂停时显示大按钮_紧急!西安老人扶梯上仰面向后晕倒,这个救命按钮很多人不知道...