二叉树(BT)、二叉查找树(BST)、平衡二叉树(AVL)、B-Tree、B+Tree、红黑树(BRT)

 

目录

树的概念

树的分类

二叉树

二叉查找树

平衡二叉树(AVL)

B-Tree(平衡多路查找树)

B树的关键特征

B+Tree

B+树关键特征

红黑树(BRT)

左旋

右旋

时间复杂度


树形结构是一类重要的非线性数据结构,其中以树和二叉树最为常见,直观来看,树是以分支关系定义的层次结构。

树的概念

树(Tree)是n(n≥0)个节点的有限集。在任意一棵非空树中:

1、有且仅有一个特定的成为根(Root)的节点

2、当n>1时,其余节点可分为m(m>0)个互不相交的有限集T_{1},T_{2},...,T_{m},其中每个集合本身又是一棵树,并且称为根的子树。

树的分类

二叉树

二叉树是一种典型的属性结构,它的特点是每个节点之多只有两个子树,并且二叉树的子树有左右之分,其次序不能任意颠倒。

二叉查找树

二叉查找树具有以下性质:左子树的值小于根节点的值,右子树的值大于根节点,下图就是一棵二叉查找树。

在这里插入图片描述

对该二叉树的节点进行查找发现深度为1的节点的查找次数为1,深度为2的查找次数为2,深度为n的节点的查找次数为n,因此其平均查找次数为 (1+2+2+3+3+3) / 6 = 2.3次

二叉查找树可以任意地构造,同样是2,3,5,6,7,8这六个数字,也可以按照下图的方式来构造:

但是这棵二叉树的查询效率就低了。因此若想二叉树的查询效率尽可能高,需要这棵二叉树是平衡的,从而引出新的定义——平衡二叉树,或称AVL树。

平衡二叉树(AVL)

平衡二叉树是基于二分法的策略提高数据的查找速度的二叉树的数据结构,平衡二叉树(AVL树)在符合二叉查找树的条件下,还满足任何节点的两个子树的高度最大差<=1,下面两张图,左边的是AVL树,它的任何节点的两个子树的高度差<=1;右边的不是AVL树,其根节点的左子树高度为0 而右子树的高度为5

总结平衡二叉树特点:

(1)非叶子节点最多拥有两个子节点;

(2)非叶子节值大于左边子节点、小于右边子节点;

(3)树的左右两边的层级数相差不会大于1;

(4)没有值相等重复的节点;

如果在AVL树中进行插入或删除节点,可能导致AVL树失去平衡,这种失去平衡的二叉树可以概括为四种姿态:LL(左左)、RR(右右)、LR(左右)、RL(右左)。它们的示意图如下。

①LL:又单旋转

②RR:左单旋转

③LR平衡旋转 先左后右

④RL平衡旋转 先右后左

平衡二叉树查找过程等同于二叉排序树相同,因此平衡二叉树查找长度不超过数的长度,即其平均查找长度为O(log2n)。

B-Tree(平衡多路查找树)

B-Tree是为磁盘等外存储设备设计的一种平衡查找树。因此在讲B-Tree之前先了解下磁盘的相关知识。

系统从磁盘读取数据到内存时是以磁盘块(block)为基本单位的,位于同一个磁盘块中的数据会被一次性读取出来,而不是需要什么取什么。

InnoDB存储引擎中有页(Page)的概念,页是其磁盘管理的最小单位。InnoDB存储引擎中默认每个页的大小为16KB,可通过参数innodb_page_size将页的大小设置为4K、8K、16K,在MySQL中可通过如下命令查看页的大小:

mysql> show variables like 'innodb_page_size';

 

而系统一个磁盘块的存储空间往往没有这么大,因此InnoDB每次申请磁盘空间时都会是若干地址连续磁盘块来达到页的大小16KB。InnoDB在把磁盘数据读入到磁盘时会以页为基本单位,在查询数据时如果一个页中的每条数据都能有助于定位数据记录的位置,这将会减少磁盘I/O次数,提高查询效率。

B-Tree结构的数据可以让系统高效的找到数据所在的磁盘块。为了描述B-Tree,首先定义一条记录为一个二元组[key, data] ,key为记录的键值,对应表中的主键值,data为一行记录中除主键外的数据。对于不同的记录,key值互不相同。

一棵m阶的B-Tree有如下特性: 
1. 每个节点最多有m个孩子。 
2. 除了根节点和叶子节点外,其它每个节点至少有Ceil(m/2)个孩子。 
3. 若根节点不是叶子节点,则至少有2个孩子 
4. 所有叶子节点都在同一层,且不包含其它关键字信息 
5. 每个非终端节点包含n个关键字信息(P0,P1,…Pn, k1,…kn) 
6. 关键字的个数n满足:ceil(m/2)-1 <= n <= m-1 
7. ki(i=1,…n)为关键字,且关键字升序排序。 
8. Pi(i=1,…n)为指向子树根节点的指针。P(i-1)指向的子树的所有节点关键字均小于ki,但都大于k(i-1)

B-Tree中的每个节点根据实际情况可以包含大量的关键字信息和分支,如下图所示为一个3阶的B-Tree:

每个节点占用一个盘块的磁盘空间,一个节点上有两个升序排序的关键字和三个指向子树根节点的指针,指针存储的是子节点所在磁盘块的地址。两个关键词划分成的三个范围域对应三个指针指向的子树的数据的范围域。以根节点为例,关键字为17和35,P1指针指向的子树的数据范围为小于17,P2指针指向的子树的数据范围为17~35,P3指针指向的子树的数据范围为大于35。

模拟查找关键字29的过程:

1. 根据根节点找到磁盘块1,读入内存。【磁盘I/O操作第1次】

2. 比较关键字29在区间(17,35),找到磁盘块1的指针P2。

3. 根据P2指针找到磁盘块3,读入内存。【磁盘I/O操作第2次】

4. 比较关键字29在区间(26,30),找到磁盘块3的指针P2。

5. 根据P2指针找到磁盘块8,读入内存。【磁盘I/O操作第3次】

6. 在磁盘块8中的关键字列表中找到关键字29。

分析上面过程,发现需要3次磁盘I/O操作,和3次内存查找操作。由于内存中的关键字是一个有序表结构,可以利用二分法查找提高效率。而3次磁盘I/O操作是影响整个B-Tree查找效率的决定因素。B-Tree相对于AVLTree缩减了节点个数,使每次磁盘I/O取到内存的数据都发挥了作用,从而提高了查询效率。

B树的关键特征

  1. 关键字集合分布在整颗树中;
  2. 任何一个关键字出现且只出现在一个结点中;
  3. 搜索有可能在非叶子结点结束;
  4. 其搜索性能等价于在关键字全集内做一次二分查找;

B+Tree

B+Tree是在B-Tree基础上的一种优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用B+Tree实现其索引结构。

从上一节中的B-Tree结构图中可以看到每个节点中不仅包含数据的key值,还有data值。而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。在B+Tree中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度。

B+Tree相对于B-Tree有几点不同:

1. 非叶子节点只存储键值信息。

2. 所有叶子节点之间都有一个链指针。

3. 数据记录都存放在叶子节点中。

将上一节中的B-Tree优化,由于B+Tree的非叶子节点只存储键值信息,假设每个磁盘块能存储4个键值及指针信息,则变成B+Tree后其结构如下图所示:

通常在B+Tree上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构。因此可以对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找。

可能上面例子中只有22条数据记录,看不出B+Tree的优点,下面做一个推算:

InnoDB存储引擎中页的大小为16KB,一般表的主键类型为INT(占用4个字节)或BIGINT(占用8个字节),指针类型也一般为4或8个字节,也就是说一个页(B+Tree中的一个节点)中大概存储16KB/(8B+8B)=1K个键值(因为是估值,为方便计算,这里的K取值为〖10〗^3)。也就是说一个深度为3的B+Tree索引可以维护10^3 * 10^3 * 10^3 = 10亿 条记录。

实际情况中每个节点可能不能填充满,因此在数据库中,B+Tree的高度一般都在2~4层。mysql的InnoDB存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只需要1~3次磁盘I/O操作。

数据库中的B+Tree索引可以分为聚集索引(clustered index)和辅助索引(secondary index)。上面的B+Tree示例图在数据库中的实现即为聚集索引,聚集索引的B+Tree中的叶子节点存放的是整张表的行记录数据。辅助索引与聚集索引的区别在于辅助索引的叶子节点并不包含行记录的全部数据,而是存储相应行数据的聚集索引键,即主键。当通过辅助索引来查询数据时,InnoDB存储引擎会遍历辅助索引找到主键,然后再通过主键在聚集索引中找到完整的行记录数据。

B+树关键特征

  1. 有n棵子树的非叶子结点中含有n个关键字(b树是n-1个),这些关键字不保存数据,只用来索引,所有数据都保存在叶子节点(b树是每个关键字都保存数据)。
  2. 所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
  3. 所有的非叶子结点可以看成是索引部分,结点中仅含其子树中的最大(或最小)关键字。
  4. 通常在b+树上有两个头指针,一个指向根结点,一个指向关键字最小的叶子结点。
  5. 同一个数字会在不同节点中重复出现,根节点的最大元素就是b+树的最大元素。

红黑树(BRT)

红黑树是一个平衡的二叉树,但不是一个完美的平衡二叉树。虽然我们希望一个所有查找都能在~lgN次比较内结束,但是这样在动态插入中保持树的完美平衡代价太高,所以,我们稍微放松一下限制,希望找到一个能在对数时间内完成查找的数据结构。这个时候,红黑树站了出来。
阅读以下需要了解普通二叉树的插入以及删除操作。
红黑树是在普通二叉树上,对没个节点添加一个颜色属性形成的,同时整个红黑二叉树需要同时满足一下五条性质
红黑树需要满足的五条性质:
性质一:节点是红色或者是黑色;
在树里面的节点不是红色的就是黑色的,没有其他颜色,要不怎么叫红黑树呢,是吧。
性质二:根节点是黑色;
根节点总是黑色的。它不能为红。
性质三:每个叶节点(NIL或空节点)是黑色;
这个可能有点理解困难,可以看图:

这里写图片描述

这个图片就是一个红黑树,NIL节点是个空节点,并且是黑色的。
性质四:每个红色节点的两个子节点都是黑色的(也就是说不存在两个连续的红色节点);
就是连续的两个节点不能是连续的红色,连续的两个节点的意思就是父节点与子节点不能是连续的红色。

性质五:从任一节点到其每个叶节点的所有路径都包含相同数目的黑色节点

这里写图片描述

 

从根节点到每一个NIL节点的路径中,都包含了相同数量的黑色节点。
这五条性质约束了红黑树,可以通过数学证明来证明,满足这五条性质的二叉树可以将查找删除维持在对数时间内。
当我们进行插入或者删除操作时所作的一切操作都是为了调整树使之符合这五条性质。
下面我们先介绍两个基本操作,旋转。
旋转的目的是将节点多的一支出让节点给另一个节点少的一支,旋转操作在插入和删除操作中经常会用到,所以要熟记。

左旋

右旋

下面讲讲插入

我们先明确一下各节点的叫法

这里写图片描述

因为要满足红黑树的这五条性质,如果我们插入的是黑色节点,那就违反了性质五,需要进行大规模调整,如果我们插入的是红色节点,那就只有在要插入节点的父节点也是红色的时候违反性质四或者是当插入的节点是根节点时,违反性质二,所以,我们把要插入的节点的颜色变成红色。

下面是可能遇到的插入的几种状况:
1、当插入的节点是根节点时,直接涂黑即可;
2、当要插入的节点的父节点是黑色的时候。
这里写图片描述
这个时候插入一个红色的节点并没有对这五个性质产生破坏。所以直接插入不用在进行调整操作。

3、如果要插入的节点的父节点是红色且父节点是祖父节点的左支的时候。
这个要分两种情况,一种是叔叔节点为黑的情况,一种是叔叔节点为红的情况。
当叔叔为黑时,也分为两种情况,一种是要插入的节点是父节点的左支,另一种是要插入的节点是父亲的右支。
我们先看一下当要插入的节点是父节点的左支的情况:

这里写图片描述
这个时候违反了性质四,我们就需要进行调整操作,使之符合性质四,我们可以通过对祖父节点进行右旋同时将祖父节点和父节点的颜色进行互换,这样就变成了:
这里写图片描述
经过这样的调整可以符合性质四并且不对其他性质产生破坏。
当插入的节点是父节点的右支的时候:
这里写图片描述
当要插入的节点是父节点的右支的时候,我们可以先对父节点进行左旋,变成如下:
这里写图片描述
如果我们把原先的父节点看做是新的要插入的节点,把原先要插入的节点看做是新的父节点,那就变成了当要插入的节点在父节点的左支的情况,对,是的,就是按照当要插入的节点在父节点的左支的情况进行旋转,旋转完之后变成如下:
这里写图片描述
4、如果要插入的节点的父节点是红色且父节点是祖父节点的右支的时候;
这个时候的情况跟情况3所表述的情况是一个镜像,将情况3的左和右互换一下就可以了。
5、如果要插入的节点的父节点是红色并且叔叔节点也为红色,如下:
这里写图片描述
这个时候,只需将父亲节点和叔叔节点涂黑,将祖父节点涂红。

以上就是插入的全部过程。
下面我们再讲讲删除的操作:

首先你要了解普通二叉树的删除操作:
1.如果删除的是叶节点,可以直接删除;
2.如果被删除的元素有一个子节点,可以将子节点直接移到被删除元素的位置;
3.如果有两个子节点,这时候就可以把被删除元素的右支的最小节点(被删除元素右支的最左边的节点)和被删除元素互换,我们把被删除元素右支的最左边的节点称之为后继节点(后继元素),然后在根据情况1或者情况2进行操作。如图:
这里写图片描述
将被删除元素与其右支的最小元素互换,变成如下图所示:

这里写图片描述
然后再将被删除元素删除:

这里写图片描述

我们下面所称的被删除元素,皆是指已经互换之后的被删除元素
加入颜色之后,被删除元素和后继元素互换只是值得互换,并不互换颜色,这个要注意。

下面开始讲一下红黑树删除的规则:
1.当被删除元素为红时,对五条性质没有什么影响,直接删除。
2.当被删除元素为黑且为根节点时,直接删除。
3.当被删除元素为黑,且有一个右子节点为红时,将右子节点涂黑放到被删除元素的位置,如图:

这里写图片描述
变成
这里写图片描述

4.当被删除元素为黑,且兄弟节点为黑,兄弟节点两个孩子也为黑,父节点为红,此时,交换兄弟节点与父节点的颜色;NIL元素是指每个叶节点都有两个空的,颜色为黑的NIL元素,需要他的时候就可以把它看成两个黑元素,不需要的时候可以忽视他。
如图:

这里写图片描述
变成:
这里写图片描述
5.当被删除元素为黑、并且为父节点的左支,且兄弟颜色为黑,兄弟的右支为红色,这个时候需要交换兄弟与父亲的颜色,并把父亲涂黑、兄弟的右支涂黑,并以父节点为中心左转。如图:
由:

这里写图片描述
变成:

这里写图片描述
6.当被删除元素为黑、并且为父节点的左支,且兄弟颜色为黑,兄弟的左支为红色,这个时候需要先把兄弟与兄弟的左子节点颜色互换,进行右转,然后就变成了规则5一样了,在按照规则5进行旋转。如图:

这里写图片描述
先兄弟与兄弟的左子节点颜色互换,进行右转,变成:

这里写图片描述
然后在按照规则5进行旋转,变成:
这里写图片描述
7.当被删除元素为黑且为父元素的右支时,跟情况5.情况6 互为镜像。
8.被删除元素为黑且兄弟节点为黑,兄弟节点的孩子为黑,父亲为黑,这个时候需要将兄弟节点变为红,再把父亲看做那个被删除的元素(只是看做,实际上不删除),看看父亲符和哪一条删除规则,进行处理变化如图:
由:
这里写图片描述
变成:
这里写图片描述
8.当被删除的元素为黑,且为父元素的左支,兄弟节点为红色的时候,需要交换兄弟节点与父亲结点的颜色,以父亲结点进行左旋,就变成了情况4,在按照情况四进行操作即可,变化如下:
由:

这里写图片描述
交换兄弟节点与父亲结点的颜色,以父亲结点进行左旋 变成:
这里写图片描述
在按照情况四进行操作,变成:
这里写图片描述

好了,删除的步骤也讲完,没有讲到的一点就是,在添加删除的时候,时刻要记得更改根元素的颜色为黑。
这里并没有语言实现,只是讲了一下红黑树的插入删除步骤,你可以根据步骤自己把红黑树实现。

相比于二叉树优缺点

AVL树是严格的平衡二叉树,平衡条件必须满足(所有结点的左右子树高度差不超过1)。不管我们是执行插入还是删除操作,只要不满足左右子树高度差不超过1的条件,就要通过旋转来保存平衡,而因为旋转非常耗时,由此我们可以知道AVL树适合用于插入与删除次数比较少,但查找多的情况。,红黑树每个节点增加一个存储位表示结点的颜色,可以是红或黑(非红即黑)。通过对任何一条从根到叶子的路径上各个节点着色的方式的限制,红黑树确保没有一条路径会比其他路径长出两倍且从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。因此,红黑树是一中弱平衡二叉树(由于是弱平衡,可以看到,在相同的节点情况下,AVL树的高度低于红黑树),相对于要求严格的AVL树来说,它的旋转次数少,插入最多两次旋转,删除最多三次旋转,所以对于搜索,插入,删除操作较多的情况下,我们就用红黑树。

时间复杂度

下面是各个类型属性结构的时间复杂度。

参考文章:https://www.cnblogs.com/ybf-yyj/p/9513706.html

https://blog.csdn.net/Hubery_James/article/details/80565137

https://blog.csdn.net/sun_tttt/article/details/65445754

 

 

 

 

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值