算法与数据结构(三) 二叉树的遍历及其线索化(Swift版)
前面两篇博客介绍了线性表的顺序存储与链式存储以及对应的操作,并且还聊了栈与队列的相关内容。本篇博客我们就继续聊数据结构的相关东西,并且所涉及的相关Demo依然使用面向对象语言Swift来表示。本篇博客我们就来介绍树结构的一种:二叉树。在之前的博客中我们简单的聊了一点树的东西,树结构的特点是除头节点以外的节点只有一个前驱,但是可以有一个或者多个后继。而二叉树的特点是除头结点外的其他节点只有一个前驱,节点的后继不能超过2个。
本篇博客,我们只对二叉树进行讨论。在本篇博客中,我们对二叉树进行创建,然后进行各种遍历,最后将二叉树进行线索化。在Demo实现之前,我们先对二叉树的概念及其特性进行介绍,然后在给出具体的代码实现。
一、二叉树的特性
上面我们已经提到过,一个除头结点外,每个节点只有一个前驱,有零到两个后继的树即为二叉树。在二叉树中,一个节点可以有左节点或者左子树,也可以有右节点或者右子树。一些特殊的二叉树,比如斜二叉树、满二叉树、完全二叉树等等就不做过多赘述了。说这么多,不如看一张图来的直观。下方就是一个典型的二叉树。
了解二叉树,理解其特性还是比较重要的。基于二叉树本身的逻辑结构,下方是二叉树这种数据结构所具备的特性。
- 特性1:在二叉树的第i层上至多有2^(i-1)(i >= 1)个节点。
- 这一特性比较好理解,如果层数是从零开始数的话,那么低i层上的节点数就是2^i,因为二叉树层与层之间的节点数是以2的指数幂进行增长的。如果根节点算是第0层的话,那么第n层的节点数就是2^n次幂。
- 特性2:深度为k的二叉树至多有2^k-1(k>=1)个节点。
- 这一特性也是比较好理解的, 由数学上的递加公式就可以很容易的推出来。由特性1易知每层最多有多少个节点,那么深度为k的话,说明一共有k层,那么共有节点数为:2^0 + 2^1 + 2^2 + 2^(k-1) = 2^k - 1。
- 特性3:二叉树的叶子节点数为n0, 度为2的节点数为n2, 那么n0 = n2 + 1。
- 这一特性也不难理解,推出n0 = n2 + 1这个公式并不难。我们假设叶子节点,也就是度数为0的节点的个数为n0, 度数为1的节点为n1, 度数为2的节点n2。那么二叉树的节点总数 n = n0 + n1 + n2。因为除了根节点外其余的节点入度都为1,所以二叉树的度数为n-1,当然度的个数可以使用出度来算,即为2*n2+n1,所以n-1=2*n2+n1。以n=n0+n1+n2与n-1=2*n2+n1这两个公式我们很容易的推出n0 = n2 + 1。
- 特性4:具有n个结点的完全二叉树的深度为log2n + 1 (向下取整,比如3.5,就取3)。
- 这个特性也是比较好理解的,基于完全二叉树的特点,我们假设完全二叉树的深度为k, 那么二叉树的结点个数的范围为2(k-1)-1 <= n <= 2k-1。由这个表达式我们很容易推出特性4。
二、二叉树的创建
上面介绍完二叉树的特性后,接下来我们要做的就是将二叉树进行存储。当然一般存储二叉树的结构是以二叉链表的形式来存储的。二叉链表的结构类似于双向链表,二叉链表的节点也是有两个结点指针的,一个指向左子树,一个指向右子树。接下来我们要使用二叉链表的形式来存储我们的二叉树。
1.先序创建二叉树
在创建二叉树之前,我们先了解一个什么是先序遍历。先序遍历就是先遍历根结点,然后遍历左子树,最后遍历右子树。我们就以此规则来创建二叉树,换句话说,我们有一个数据序列,将依照这个序列按照先序创建二叉树的原则来创建该二叉树,先创建二叉树的根节点,然后再创建二叉树的左子树,然后再创建右子树。而这个创建的二叉树的先序遍历的结果就是我们之前输入的数据序列。下方就是先序创建二叉树的原理图。
从上面的分析我们不难看出,我们要先创建根节点,然后创建左子树,最后创建右子树。因为左子树和右子树都是二叉树,所以创建左子树和右子树是原问题的子问题。也就是说子问题与原问题解决方案一致,这种情况下就可以使用递归的思想来解决。我们先将上述二叉树的结构转换成二叉链表的形式直观的感受一下,然后再将其使用代码的形式进行表示即可。下方这个截图就是上述二叉树的二叉链表的存储结构。每个节点都有左指针与右指针,分别自己的左子节点和右子节点。如果没有子节点就为空。
2.先序创建二叉树的代码实现
上面我们分析了二叉链表的结构,接下来我们就来创建二叉链表了。首先我们得创建二叉链表的节点类,之前我们用C语言来实现二叉树的时候,是使用的结构体来实现的二叉链表的节点,因为C语言是面向过程的语言,根本就没有类这个概念。因为此刻我们是使用的面向对象语言,所以我就可以使用一个类来表示我们二叉链表的节点了。下方这个GeneralBinaryTreeNote就是二叉链表的类。data属性存储的就是树节点中所存储的值,而leftChild就指向左节点的内存地址,而rightChild就指向右节点的内存地址。
上面我们已经说过,先序创建二叉树的过程是可以用递归来表示的,所以我们就递归的去创建我们想要创建的二叉树。下方就是先序创建二叉树的核心代码,self.items中存储的是二叉树的节点信息。经过下方函数的递归执行,就可以创建出我们想要的二叉树了。从下方的递归过程我们就明显的能看出是先序创建的二叉树。先创建的根节点,然后递归创建左子树,然后在递归创建右子树。
下方就是我们二叉树的初始化过程,下方在初始化过程中主要是调用上方的这个方法,将items数组中存储的值转换成二叉链表的存储结构。items数组中的空字符串,表明该节点为空。
其实上面实例中所创建的二叉树的结构就是下方的结构。
三、二叉树的遍历
聊二叉树怎么能没有二叉树的遍历呢,下方就会给出几种常见的二叉树的遍历方法。在遍历二叉树的方法中一般有先序遍历,中序遍历,后续遍历,层次遍历。本篇博客主要给出前三种遍历方式,而层次遍历会在图的部分进行介绍。二叉树的层次遍历其实与图的广度搜索是一样的,所以这部分放到图的相关博客中介绍。下方会给出几种遍历的具体方式,然后给出具体的代码实现。
二叉树的先、中、后遍历,这个先中后指的是遍历根节点的先后顺序。先序遍历:根左右,中序遍历:左根右,后序遍历:左右根。下方将详细介绍到。
1.先序遍历
关于先序遍历,上面已经介绍过一些了,接下来再进行细化一下。先序遍历,就是先遍历根节点然后再遍历左子树,最后遍历右子树。下图就是我们上面创建的二叉树的先序遍历的顺序,由下方的示例图就可以看出先序遍历的规则。一句话总结下方的结构图:根节点->左节点->右节点。下方先序遍历的顺序为:A B D 空 空 E 空 空 C 空 F 空 空 。
上面给出了原理,接下来又到了代码实现的时候了。在树的遍历时,我们依然是采用递归的方式,因为无论是左子树还是右子树,都是二叉树的范畴。所以在进行二叉树遍历时,可以使用递归遍历的形式。而先序遍历莫非就是先遍历根节点,然后递归遍历左子树,最后遍历右子树。下方就是先序遍历的代码实现。在下方代码中,如果左节点或者右节点为空,那么我们就输出“空”。
2.中序遍历
中序遍历,与先序遍历的不同之处在于,中序遍历是先遍历左子树,然后遍历根节点,最后遍历右子树。一句话总结:左子树->根节点->右子树。下方就是我们之前创建的树的中序遍历的结构图以及中序遍历的结果。
中序遍历的代码实现与先序遍历的代码实现类似,都是使用递归的方式来实现的,只不过是先递归遍历左子树,然后遍历根节点,最后遍历右子树。下方就是中序遍历的代码具体实现。
3.后序遍历
接下来聊一下二叉树的后序遍历。如果上面这两种遍历方式理解的话,那么后序遍历也是比较好理解的。后序遍历是先遍历左子树,然后再遍历右子树,最后遍历根节点。与上方的表示方法一直,首先我们给出表示图,如下所示:
后序遍历的代码就不做过多赘述了,与之前两种依然类似,只是换了一下遍历的顺序。下方就是二叉树后序遍历的代码实现。
4、层次遍历
二叉树的层次遍历就不是二叉树这种数据结构所独有的了。后面的博客中我们会介绍到图这种数据结构,在图中有一个广度搜索,放到二叉树中就是层次遍历。也就是说二叉树的层次遍历,就是图中以二叉树的根节点为起始节点的广度搜索(BFS)。本篇博客就不给出具体的代码了,后面的博客会给出BFS的具体算法。当然在之前的博客中有图的BFS以及DFS。不过是C语言的实现。下方就是二叉树层次遍历的实例图。
四、二叉树的线索化
二叉树的线索化,起始就是利用二叉树中的空的节点来将二叉树转换成链表的结构。当然只针对中序遍历的序列。从上面中序遍历的结果中,我们不难看出,有节点的值与空指针是间隔的(空 D 空 B 空 E 空 A 空 C 空 F 空)。也就是说好多空的左指针与右指针浪费了。二叉树的线索化,就是在中序遍历中,将空的左子树的指针指向其中序遍历结果的前驱,而空的右子树指针指向中序遍历中该节点的后继。具体的示意图如下所示:
从上面的图中我们不难看出。在被线索化的二叉树中,左节点指针不止指向左节点,而且有可能指向节点的前驱。而右节点指针不仅仅是指向右节点的指针,还有可能指向该节点在中序遍历中的后继节点。为了标记指针是指向子节点还是指向前驱或者后继,所以我们要添加相应的标志位来标记指针指向的是那些节点。下方就是我们改造后的二叉树的节点:
改造完节点后,我们就可以将二叉树进行线索化了,下方就是被线索话的二叉树的代码。可以看出,下方的代码的整体步骤与二叉树的中序遍历类似。
被线索化的二叉树就可以根据我们添加的线索进行中序遍历了,效率要比递归的中序遍历要高的多,如下所示:
五、测试用例
上面的代码都是如何去实现了,接下来到了我们测试的时间了,下方这段代码段是我们的测试用例。首先给出二叉树的节点信息,然后先序的创建一棵二叉树。然后给出二叉树的先、中、后续遍历,最后给出二叉树线索话的结果。
下方截图就是我们测试用例的运行结果,一目了然,在此就不做过多的赘述了。
本篇博客的篇幅也够长的了,就先到这儿吧,上述实例的完整Demo会在github上进行分享, 下篇博客我们将要介绍图的邻接链表和邻接矩阵,以及图的BFS和DFS。
github链接地址:https://github.com/lizelu/DataStruct-Swift/tree/master/BinaryTree
转载于:https://www.cnblogs.com/ludashi/p/5976682.html
算法与数据结构(三) 二叉树的遍历及其线索化(Swift版)相关推荐
- 《数据结构与算法》——树与二叉树之遍历总结
<数据结构与算法>--树与二叉树之遍历总结 树与二叉树部分计划分为三次进行复习总结,第一次为基本概念和二叉树的遍历,第二次内容为线索二叉树以及树和森林,第三次为树与二叉树的应用. 目录 & ...
- 某酷ckey签名生成算法系列--(三)ast代码控制流平坦化
某酷ckey签名生成算法系列--(三)ast代码控制流平坦化 观察三个switch的值分别是Ci.mi和Ai.而这三个值又因为li的确定而确定的.也就是说已知li的值,就可以分别计算出Ci.mi和Ai ...
- C语言实现二叉树的中序线索化及遍历中序线索二叉树
C语言实现二叉树的线索化以及如何遍历线索二叉树! 文章目录 线索二叉树的结构及数据类型定义 根据输入结点初始化二叉树 中序遍历二叉树并线索化 遍历中序线索二叉树 项目完整代码 项目完整代码(改进版) ...
- 《数据结构与算法》实验报告——二叉树的遍历
<数据结构>实验报告 学号:2018329621200 机房号10-414 姓名: 申屠志刚 日期:2019/11/4 程序名:main.cpp 实验内容: 二叉树的遍历 一.目的和要求( ...
- PTA数据结构与算法题目集 6-9 二叉树的遍历
PTA数据结构与算法题目集(中文) 6-9 二叉树的遍历 void InorderTraversal( BinTree BT ){if(BT==NULL)return;if(BT->Left){ ...
- 由任意二叉树的前序遍历序列和中序遍历序列求二叉树的思想方法_算法与数据结构基础 - 二叉树(Binary Tree)...
二叉树基础 满足这样性质的树称为二叉树:空树或节点最多有两个子树,称为左子树.右子树, 左右子树节点同样最多有两个子树. 二叉树是递归定义的,因而常用递归/DFS的思想处理二叉树相关问题,例如Leet ...
- 算法与数据结构--空间复杂度O(1)遍历树
大家好~我叫「小鹿鹿鹿」,是本卖萌小屋的第二位签约作(萌)者(货).和小夕一样现在在从事NLP相关工作,希望和大家分享NLP相关的.不限于NLP的各种小想法,新技术.这是我的第一篇试水文章,初来乍到, ...
- c++ 删除二叉树的子树_数据结构—树|二叉树|前序遍历、中序遍历、后序遍历【图解实现】...
点击蓝字关注我们 AI研习图书馆,发现不一样的精彩世界 数据 结构 二叉树的遍历 一.树 在谈二叉树的知识点之前,我们首先来看一下树和图的基本概念.树:不包含回路的连通无向图,树是一种简单的非线性结构 ...
- java数据结构二叉树遍历_java数据结构 之 二叉树的遍历(1)
树(tree)是一种抽象数据类型(ADT),用来模拟具有树状结构性质的数据集合.它是由n(n>0)个有限节点通过连接它们的边组成一个具有层次关系的集合 节点:上图的圆圈,比如A,B,C等都是表示 ...
最新文章
- iOS 之 tableView的复用、设计模式
- springboot文字转语音(jacob)
- 为什么 StringBuilder 不是线程安全的?
- Oracle apex 搭建
- NYOJ541 最强DE 战斗力(第五届省赛试题)
- 使用扩展技术对SAP Fiori应用进行端到端的增强,一个实际案例介绍
- ZKWeb网站框架的动态编译的实现原理
- linux 命令/目录 名称 英文单词 缩写 助记
- js 加总数组中某一列_js根据对象数组中某一属性值,合并相同项,并对某一属性累加处理...
- 高考数据分析和读书感悟
- hibernate第一天
- java乱码中文变为问号_转 java 中文 乱码 问号
- 手机微信桌面计算机不见了怎么办,微信桌面图标不见了怎么办
- 传奇SF的架设-开外网
- 上传文件到服务器指令,上传文件到远程服务器的命令
- pycharm贪吃蛇
- R文本挖掘-文章关键词提取
- bz2 — bzip2 压缩
- Lammps反应力场信息
- 黑莓Curve 9360拥有NFC技术
热门文章
- 【51nod - 1108】距离之和最小 V2(曼哈顿距离,中位数性质)
- 【HDU - 3068】最长回文(Manacher算法,马拉车算法求最长回文子串)
- 【C语言实现反转数组】(用栈实现)51nod - 训练营
- 50个最有用的Matplotlib数据分析与可视化图
- android action bar 风格,自定义ActionBar风格和样式
- 第九大陆服务器未找到文件,第九大陆服务器优化规则说明 拍卖行和寄售功能关闭公告...
- 关于ajax请求400问题解决
- mysql archieve_mysql的archive储存引擎
- vue element-ui登录页面源码
- 初学多线程使用中踩过的坑