算法与数据结构(三) 二叉树的遍历及其线索化(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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/457096.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux替换某个文件夹下所有文件,Linux 批量查找并替换文件夹下所有文件的内容...

1.批量查找某个目下文件的包含的内容cd etcgrep -rn "查找的内容" ./2.批量替换某个目下所有包含的文件的内容cd etcsed -i "s/查找的内容/替换后的内容/g" grep -rl "查找的内容" ./3.批量查找并替换任意文件夹下的文件内容。sed -i "s/要…

java中String相等问题

判断两个字符串是否相等的问题。在编程中&#xff0c;通常比较两个字符串是否相同的表达式是“”,但在java中不能这么写。在java中&#xff0c;用的是equals(); 例&#xff1a;A字符串和B和字符串比较: if(A.equals(B)){ } 返回true 或false. String 的equals 方法用于比较两个…

WebForm 分页与组合查询

1.封装实体类 2.写查询方法 //SubjectData类 public List<Subject> Select(string name){List<Subject> list new List<Subject>();cmd.CommandText "select *from Subject where SubjectName like a ";cmd.Parameters.Clear();cmd.Parameters.A…

node.js简单爬虫

这里假设你已经安装好node.js和npm&#xff0c;如果没有安装&#xff0c;请参阅其他教程安装。 配置首先是来配置package.json文件&#xff0c;这里使用express,request和cheerio。package.json如下&#xff1a; {"name": "node-scrape","version&quo…

Struts2入门(二)——配置拦截器

一、前言 之前便了解过&#xff0c;Struts 2的核心控制器是一个Filter过滤器&#xff0c;负责拦截所有的用户请求&#xff0c;当用户请求发送过来时&#xff0c;会去检测struts.xml是否存在这个action&#xff0c;如果存在&#xff0c;服务器便会自动帮我们跳转到指定的处理类中…

linux固态机械分区吗,不再疑惑!实测数据后才知道固态硬盘究竟要不要分区

不再疑惑&#xff01;实测数据后才知道固态硬盘究竟要不要分区2019-12-10 20:52:00162点赞594收藏177评论前几年的固态硬盘价格昂贵&#xff0c;一般用户会选择128G或256G的固态作为系统盘&#xff0c;由于单盘空间不大&#xff0c;一般都会配合机械硬盘使用&#xff0c;无需考…

安卓手机的后门控制工具SPADE

SPADE&#xff0c;一款安卓手机的后门控制工具&#xff0c;安全研究人员可以以此了解和研究安卓后门原理。 首先&#xff0c;我们从网站www.apk4fun.com下载apk文件&#xff0c;如ccleaner。然后&#xff0c;我们安装spade git clone https://github.com/suraj-root/spade.git …

MySQL案例-open too many files,MyISAM与partition

-------------------------------------------------------------------------------------------------短文---------------------------------------------------------------------------------------------------------------长话短说~现象: error log中批量刷错误日志, 形…

linux网卡有很多error,教你设置win7系统虚拟机安装linux提示network error的解决方法...

很多朋友在使用电脑的过程中&#xff0c;会发现win7系统虚拟机安装linux提示network error的现象&#xff0c;当遇到win7系统虚拟机安装linux提示network error的问题&#xff0c;我们要怎么解决呢&#xff1f;如今还有很多用户不知道如何处理win7系统虚拟机安装linux提示netwo…

linux模拟网络延迟,使用Nistnet搭建网络延迟模拟设备 (network delay simulator)

mknod /dev/hitbox c 62 0mknod /dev/nistnet c 62 1chown root /dev/hitboxchown root /dev/nistnetmknod /dev/mungebox c 63 0chown root /dev/mungeboxmknod /dev/spybox c 64 0chown root /dev/spyboxmodprobe nistnet可以将这个放到/etc/rc.local中&#xff0c;以便重启后…

将本地Blog部署到GitHub上,有自己的博客页面!

前言 上一篇文章我们已经把本地的hexo环境搭建好了&#xff0c;并且在本地成功预览&#xff0c;但是本地预览也意味着自己的博文只能自己看的到&#xff0c;其他人根本看不到&#xff0c;这篇文章将接上文说一说如何把本地Blog部署到GitHub上&#xff0c;好让小伙伴可以来访问我…

Linux下安装配置JDK

本人使用的VM虚拟机&#xff0c;在VM上安装了Linux&#xff0c;版本是CentOS-6.7-i386-bin-DVD1.iso。 一、下载JDK 在进入JDK官网&#xff0c;找到要下载的JDK版本&#xff0c;将下载地址复制下来&#xff0c;放到迅雷中下载&#xff0c;我下载的是&#xff1a;http://downloa…

新手使用GitHub客户端提交项目的步骤

1.下载https://windows.github.com/ github客户端 2.安装完github&#xff0c;会出现 点击GitHub&#xff0c;Git Shell是命令行指令&#xff0c;暂时用不上 3.点击进入之后 输入你在https://github.com上面注册的用户名和密码点击log in 4.登录之后新建项目 点击左上角…

linux的命令uname n,Linux下uname命令及其选项

Linux下uname命令及其选项2017-03-15 23:22:26晓得了Linux系统的用户信息后&#xff0c;你也可能想晓得所登录的系统信息&#xff0c;今日就绍介获取系统本身信息的命令uname,这搭u应当是UNIX的缩写&#xff0c;操作如次&#xff1a;uname使役uname还可以得到其它相关系统的信息…

火狐浏览器Firefox如何使用插件,火狐有哪些好用的插件

1 CoorPreviews 不打开网页链接预览该网页的内容。 预览如图所示&#xff1a; 点击关闭旁边的钉子可以让该窗口保持开着并且浏览速度加快。这对于快速浏览图片时非常有用。 2 FoxTab 3D方式预览网页&#xff0c;只要按一下输入框左侧按钮即可。 此外还提供多种预览模式和其…

GitHub+Hexo搭建自己的Blog之-主题配置

前言 前两章我们已经把Blog的环境全部搭建完毕了&#xff0c;但是还没有内容&#xff0c;而且hexo默认的主题是不是感觉挺丑的&#xff0c;其实hexo给我们提供了很多主题模板&#xff0c;总有一款是你喜欢的&#xff0c;本篇文章将继续说一说如何配置主题&#xff0c;怎么创建博…

开源app之MyHearts

前言 这个月&#xff0c;说实话&#xff0c;有忙有闲&#xff0c;经历了一次病痛的洗礼&#xff0c;才认识到了只有好好的生活&#xff0c;认真的对待自己的身体&#xff0c;才能更好的去工作&#xff0c;没有了身体的支撑&#xff0c;什么工作都只能是纸老虎&#xff0c;不攻自…

前端那些事之原生 js实现贪吃蛇篇

2019独角兽企业重金招聘Python工程师标准>>> 原生js实现贪吃蛇 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>贪吃蛇游戏</title><style>body, div, img {margin: 0 auto;pa…

整理一些完全免费开放的API接口

前言 在开发测试阶段&#xff0c;或者是在写Demo的时候&#xff0c;难免会用到一些测试数据&#xff0c;有时苦于没有可用的接口&#xff0c;需要自己动手去写&#xff0c;但是这样大大降低了效率&#xff0c;前期我也找了一些开放的接口&#xff0c;这篇文章整理一下&#xff…

如何发现优秀的开源项目?

之前发过一系列有关 GitHub 的文章&#xff0c;有同学问了&#xff0c;GitHub 我大概了解了&#xff0c;Git 也差不多会使用了&#xff0c;但是 还是搞不清 GitHub 如何帮助我的工作&#xff0c;怎么提升我的工作效率&#xff1f; 问到点子上了&#xff0c;GitHub 其中一个最重…