对于程序员而言,树这种数据结构是一种比较常见的数据结构,今天小编就来介绍一下数这种数据结构,然后介绍一个简单的二叉树的C语言实现。
前驱内容
前情回顾
首先,我们先来温习一下什么是数据结构,我们之前介绍过,在实际使用数据的时候我们会把一些有关联的点组合起来进行使用,这就是有结构的数据。我们也介绍过一种简单的数据结构,这种数据结构的特点是一个数据连接着另一个数据,后一个数据连接着前一个数据。最后这些数据会像线一样连成一串,这就是线性表。
什么是树?
那么,有没有一种情况,每个数据都可以连接多个数据呢?确实存在着这样的情况,当每个数据连接着多个数据的时候,就是图(后续文章会介绍)。当每个数据后面连接着0到多个数据,而前面指连接着一个数据的时候,就是树,就像这样:
树状图是一种数据结构,它是由n(n>=1)个有限结点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
关于树的结构
我们之前有谈到数据之间的关系,对于线性表而言,一个数据只会连接另一个数据,这样的关系我们称为一对一的关系,也就是说,一个数据只会有一个后继节点。
对于数而说,一个数据后面可能会连接一个数据,也可能连接多个数据,甚至有可能一个数据都没有,这样的关系我们称为一对多的关系。
对于线性表而言,因为结构比较简单,所以数据之间互相称为前驱节点,后续节点,代表数据间的关系。而在树中,明显复杂得多。以下是对一颗数的结构描述(以下面的树状图为例):
1、结点(Node):表示树中的数据元素,由数据项和数据元素之间的关系组成。在图中,共有10个结点。
2、结点的度(Degree of Node):结点所拥有的子树的个数,在图中,结点A的度为3(注意,E,F也符合树的定义(树的定义见下文)所以B的度为2)。
3、树的度(Degree of Tree):树中各结点度的最大值(节点A和D的度都为最大值3)。上图中树的度为3。
4、叶子结点(Leaf Node):度为0的结点,也叫终端结点。上图中,结点E、F、G、H、I、J都是叶子结点。
5、分支结点(Branch Node):度不为0的结点,也叫非终端结点或内部结点。上图中,结点A、B、C、D是分支结点。
6、孩子(Child):结点子树的根。上图中,结点B、C、D是结点A的孩子。
7、双亲(Parent):结点的上层结点叫该结点的双亲。上图中,结点B、C、D的双亲是结点A。
8、祖先(Ancestor):从根到该结点所经分支上的所有结点。上图中,结点E的祖先是A和B。
9、子孙(Descendant):以某结点为根的子树中的任一结点。上图中,除A之外的所有结点都是A的子孙。
10、兄弟(Brother):同一双亲的孩子。上图中,结点B、C、D互为兄弟。
11、结点的层次(Level of Node):从根结点到树中某结点所经路径上的分支数称为该结点的层次。根结点的层次规定为1,其余结点的层次等于其双亲结点的层次加1。
12、堂兄弟(Sibling):同一层的双亲不同的结点。上图中,G和H互为堂兄弟。
13、树的深度(Depth of Tree):树中结点的最大层次数。上图中,树的深度为3。
14、无序树(Unordered Tree):树中任意一个结点的各孩子结点之间的次序构成无关紧要的树。通常树指无序树。
15、有序树(Ordered Tree):树中任意一个结点的各孩子结点有严格排列次序的树。二叉树是有序树,因为二叉树中每个孩子结点都确切定义为是该结点的左孩子结点还是右孩子结点。
16、森林(Forest):m(m≥0)棵树的集合。自然界中的树和森林的概念差别很大,但在数据结构中树和森林的概念差别很小。从定义可知,一棵树由根结点和m个子树构成,若把树的根结点删除,则树变成了包含m棵树的森林。当然,根据定义,一棵树也可以称为森林。
如何判断一个节点算不算树(树的定义)
- 有且仅有一个称为根的结点。
- 如果n>1, 除根结点以外其它结点可以分为m(m>0)个不相交的集合T1,T2,T3,T4,......,Tm,其中每一个集合都是一棵树。树T1, T2, T3,......,Tm称为这棵树的子树。
树的种类
树的种类
无序树
树的任意节点的子节点没有顺序关系。
有序树
树的任意节点的子节点有顺序关系。
二叉树
树的任意节点至多包含两棵子树。二叉树遍历:二叉树的遍历是指从二叉树的根结点出发,按照某种次序依次访问二叉树中的所有结点,使得每个结点被访问一次,且仅被访问一次。二叉树的访问次序可以分为四种:前序遍历 中序遍历 后序遍历 层次遍历
满二叉树
叶子节点都在同一层并且除叶子节点外的所有节点都有两个子节点。
![](https://atts.w3cschool.cn/attachments/day_220312/202203121603232643.jpg)
完全二叉树
对于一颗二叉树,假设其深度为d(d>1)。除第d层外的所有节点构成满二叉树,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树;
![](https://atts.w3cschool.cn/attachments/day_220312/202203121603232302.jpg)
完满二叉树
![](https://atts.w3cschool.cn/attachments/day_220312/202203121603247491.jpg)
![](https://atts.w3cschool.cn/attachments/day_220312/202203121603246479.jpg)
霍夫曼树
带权路径最短的二叉树称为哈夫曼树或最优二叉树。
二叉查找树(二叉搜索树、二叉排序树、BST)[这几个都是别名]
若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;任意节点的左、右子树也分别为二叉查找树;没有键值相等的节点。
平衡二叉树
它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树,同时,平衡二叉树必定是二叉搜索树。
AVL树
在计算机科学中,AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树本质上还是一棵二叉搜索树,它的特点是:1.本身首先是一棵二叉搜索树。2.带有平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1。也就是说,AVL树,本质上是带了平衡功能的二叉查找树(二叉排序树,二叉搜索树)。使用场景:AVL树适合用于插入删除次数比较少,但查找多的情况。也在Windows进程地址空间管理中得到了使用旋转的目的是为了降低树的高度,使其平衡
红黑树
红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:性质1. 节点是红色或黑色。性质2. 根节点是黑色。性质3. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)性质4. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。使用场景:红黑树多用于搜索,插入,删除操作多的情况下红黑树应用比较广泛:1. 广泛用在C++的STL中。map和set都是用红黑树实现的。2. 著名的linux进程调度Completely Fair Scheduler,用红黑树管理进程控制块。3.epoll在内核中的实现,用红黑树管理事件块4.nginx中,用红黑树管理timer等
伸展树
伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(log n)内完成插入、查找和删除操作。它由丹尼尔·斯立特Daniel Sleator 和 罗伯特·恩卓·塔扬Robert Endre Tarjan 在1985年发明的。在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使整个查找时间更小,被查频率高的那些条目就应当经常处于靠近树根的位置。于是想到设计一个简单方法, 在每次查找之后对树进行重构,把被查找的条目搬移到离树根近一些的地方。伸展树应运而生。伸展树是一种自调整形式的二叉查找树,它会沿着从某个节点到树根之间的路径,通过一系列的旋转把这个节点搬移到树根去。它的优势在于不需要记录用于平衡树的冗余信息。
替罪羊树
替罪羊树是计算机科学中,一种基于部分重建的自平衡二叉搜索树。在替罪羊树上,插入或删除节点的平摊最坏时间复杂度是O(log n),搜索节点的最坏时间复杂度是O(log n)。在非平衡的二叉搜索树中,每次操作以后检查操作路径,找到最高的满足max(size(son_L),size(son_R))>alpha*size(this)的结点,重建整个子树。这样就得到了替罪羊树,而被重建的子树的原来的根就被称为替罪羊节点。替罪羊树替罪羊树是一棵自平衡二叉搜索树,由ArneAndersson提出。替罪羊树的主要思想就是将不平衡的树压成一个序列,然后暴力重构成一颗平衡的树。
B-tree(B-树或者B树)
一棵m阶B树(balanced tree of order m)是一棵平衡的m路搜索树。它或者是空树,或者是满足下列性质的树:1、根结点至少有两个子女;2、每个非根节点所包含的关键字个数 j 满足:┌m/2┐ - 1 <= j <= m - 1;3、除根结点以外的所有结点(不包括叶子结点)的度数正好是关键字总数加1,故内部子树个数 k 满足:┌m/2┐ <= k <= m ;4、所有的叶子结点都位于同一层。
![](https://atts.w3cschool.cn/attachments/day_220312/202203121603253010.jpg)
B树(B-Tree)是一种自平衡的树,它是一种多路搜索树(并不是二叉的),能够保证数据有序。同时它还保证了在查找、插入、删除等操作时性能都能保持在O(logn),为大块数据的读写操作做了优化,同时它也可以用来描述外部存储(支持对保存在磁盘或者网络上的符号表进行外部查找)
B+树
B+树是B树的一种变形形式,B+树上的叶子结点存储关键字以及相应记录的地址,叶子结点以上各层作为索引使用。一棵m阶的B+树定义如下:(1)每个结点至多有m个子女;(2)除根结点外,每个结点至少有[m/2]个子女,根结点至少有两个子女;(3)有k个子女的结点必有k个关键字。B+树的查找与B树不同,当索引部分某个结点的关键字与所查的关键字相等时,并不停止查找,应继续沿着这个关键字左边的指针向下,一直查到该关键字所在的叶子结点为止。更适合文件索引系统原因: 增删文件(节点)时,效率更高,因为B+树的叶子节点包含所有关键字,并以有序的链表结构存储,这样可很好提高增删效率使用场景:文件系统和数据库系统中常用的B/B+ 树,他通过对每个节点存储个数的扩展,使得对连续的数据能够进行较快的定位和访问,能够有效减少查找时间,提高存储的空间局部性从而减少IO操作。他广泛用于文件系统及数据库中,如:Windows:HPFS 文件系统Mac:HFS,HFS+ 文件系统Linux:ResiserFS,XFS,Ext3FS,JFS 文件系统数据库:ORACLE,MYSQL,SQLSERVER 等中B树:有序数组+平衡多叉树B+树:有序数组链表+平衡多叉树
B*树
B*树是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针;B*树定义了非叶子结点关键字个数至少为(2/3)*M,即块的最低使用率为2/3(代替B+树的1/2)。B+树的分裂:当一个结点满时,分配一个新的结点,并将原结点中1/2的数据复制到新结点,最后在父结点中增加新结点的指针;B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针;B*树的分裂:当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了);如果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针;所以,B*树分配新结点的概率比B+树要低,空间使用率更高;
字典树
又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。它有3个基本性质:根节点不包含字符,除根节点外每一个节点都只包含一个字符;从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;每个节点的所有子节点包含的字符都不相同。
线索二叉树
在二叉树的结点上加上线索的二叉树称为线索二叉树,对二叉树以某种遍历方式(如先序、中序、后序或层次等)进行遍历,使其变为线索二叉树的过程称为对二叉树进行线索化。
总结一下:
![](https://atts.w3cschool.cn/attachments/day_220312/202203121603255786.jpg)
该处内容来自知乎大佬的总结,原文章请查看:https://zhuanlan.zhihu.com/p/90255760