计算机中的树(3)
总目录传送门:
目录
红黑树(R-B Tree)
一种二叉查找树,但在每个节点增加一个存储位表示节点的颜色,可以是red或black. 通过对任何一条从根到叶子的路径上各个节点着色的方式的限制,红黑树确保没有一条路径会比其它路径长出两倍.它是一种弱平衡二叉树(由于是若平衡,可以推出,相同的节点情况下,AVL树的高度低于红黑树),相对于要求严格的AVL树来说,它的旋转次数变少,所以对于搜索,插入,删除操作多的情况下,我们就用红黑树.
性质1:每个节点要么是红色,要么是黑色。 性质2:根节点永远是黑色的。 除质3:所有的叶子节点都是空节点(即nil),并且是黑色的。 性质4:每个红色节点的两个子节点都是黑色的。(从每个叶子到根的路径上不会有两个连续的红色节点。) 性质5:从任一节点到其子树中每个叶子节点(nil节点)的路径都包含相同数量的黑色节点。
我们讨论具体的红黑树的实现之前,我们需要先讨论一下B树和B+树,因为红黑树是对概念模型2-3-4树的一种实现。
以二叉树为基础,在二叉树的属性中加入一个颜色属性来表示2-3-4树中不同的节点。
基于红黑树的平衡性质,在进行左右旋转就会变得更加的便于搜索,插入和删除可以通过下面的网站理解,引用一个CSDN的评论:
祖宗根节点必黑,允许黑连黑,不允许红连红;新增红色,爸叔通红就变色,爸红叔黑就旋转,那黑往那旋
https://rbtree.phpisfuture.com/https://rbtree.phpisfuture.com/
我们说红黑树是对概念模型2-3-4树的一种实现。所以为了深入了解红黑树,我们需要小了解B树
我们讨论B树和B+树的时候无可避免的会谈论到数据库,其中的原因就是B+树是我们最常用的数据库MYSQL索引使用的数据结构。要理解数据库的索引优化和数据库原理就会无可避免的去理解B树和B+树。
B/B+树是为了磁盘或其它存储设备而设计的一种平衡多路查找树(相对于二叉,B树每个内节点有多个分支)
与红黑树相比,在相同的的节点的情况下,一颗B/B+树的高度远远小于红黑树的高度.B/B+树上操作的时间通常由存取磁盘的时间和CPU计算时间这两部分构成,而CPU的速度非常快,所以B树的操作效率取决于访问磁盘的次数,关键字总数相同的情况下B树的高度越小,磁盘I/O所花的时间越少.
B树(B Tree)
我们讨论B树和B+树的时候无可避免的会谈论到数据库,其中的原因就是B+树是我们最常用的数据库MYSQL索引使用的数据结构。要理解数据库的索引优化和数据库原理就会无可避免的去理解B树和B+树。
B/B+树是为了磁盘或其它存储设备而设计的一种平衡多路查找树(相对于二叉,B树每个内节点有多个分支),与红黑树相比,在相同的的节点的情况下,一颗B/B+树的高度远远小于红黑树的高度.B/B+树上操作的时间通常由存取磁盘的时间和CPU计算时间这两部分构成,而CPU的速度非常快,所以B树的操作效率取决于访问磁盘的次数,关键字总数相同的情况下B树的高度越小,磁盘I/O所花的时间越少.
首先来看看B树:
B树的概念:
B-树是一种多路搜索树,他不一定是二叉的,有可能3,4等等。
它或者是空树,或者是满足下列性质的树:
1、根结点至少有两个子女;
2、每个非根节点所包含的关键字个数 j 满足:┌m/2┐ - 1 <= j <= m - 1;
3、除根结点以外的所有结点(不包括叶子结点)的度数正好是关键字总数加1,故内部子树个数 k 满足:┌m/2┐ <= k <= m ;
4、所有的叶子结点都位于同一层。
B树的查找:
B树的查找类似二叉排序树的查找,不同的是B树每个结点上是多关键码的有序表,在到达某个结点时,先在有序表中查找,若找到,则查找成功;否则,到按照对应的指针信息指向的子树中去查找,当到达叶子结点时,则说明树中没有对应的关键码,他的时间复杂度是O(nlogn)。
所以总结一下流程就是:
1)先让key与根结点中的关键字比较,如果key等于k[i](k[]为结点内的关键字数组),则查找成功
2)若key<k[1],则到p[0]所指示的子树中进行继续查找(p[]为结点内的指针数组),这里要注意B-树中每个结点的内部结构。
3)若key>k[n],则道p[n]所指示的子树中继续查找。
4)若k[i]<key<k[i+1],则沿着指针p[I]所指示的子树继续查找。
5)如果最后遇到空指针,则证明查找不成功。
如上图中,我想找58对应的data,从第一次开始比对,都不相等,就找(25,50)之间的通道然后发现27之后就是30,没有找到,证明查找不成功。
B树的插入:
要确定一下每个结点中关键字个数的范围,如果B-树的阶数为m,则结点中关键字个数的范围为ceil(m/2)-1 ~ m-1个。
对于关键字的插入,需要找到插入位置。在B树的查找过程中,当遇到空指针时,则证明查找不成功,为什么呢?因为B树相当于是在不停地进行二分比较查找,也就是说当空指针时,证明树种不存在任何一个node在新的查找范围之内,也就是二分不可再分。这就说明树中已经不存在我们索要搜索的值了。
同时我们也找到了插入的位置,即根据空指针可以确定在最底层非叶结点中的插入位置,为了方便,我们称最底层的非叶结点为终端结点,由此可见,B树结点的插入总是落在终端结点上。在插入过程中有可能破坏B树的特征,如新关键字的插入使得结点中关键字的个数超过规定个数,中间结点为m/2向上取整的位置,这是要进行按中间结点的拆分,将中间结点放到父节点,如果父节点也不满足要求,就一直到向上分裂。
举个例子,对于这个B树:我们想插入一个节点4:
我们首先会对这个树进行遍历:4<9,找右边子树,2<4<6走中间子树,下面3<4<5,走中间子树,这时候发现没有子节点了,就在当前位置进行插入操作:
节点3,5是两元素节点,没有办法再增加了,父节点2,6也是两元素节点,所以我们把根元素9升级:
已知9会变成双节点,所以2,6需要拆开到中节点,3,5拆开到子节点,树就会变成:
B树相对于B+树的优点是,如果经常访问的数据离根节点很近,而B树的非叶子节点存储关键字数据的地址,所以这种数据检索的时候会要比B+树快。
B+树(B+ Tree)
B+树最常提到的,就是这种结构作为SQL的索引底层使用。B+树就是一种特殊的B树,相对于B树来说,B+树更充分的利用了节点的空间,让查询速度更加稳定,其速度完全接近于二分法查找(logN)。
B+树是B树的一种变形形式,m阶B+树满足以下条件:
(1) 每个结点至多有m个子节点。
(2) 除根节点和叶结点外,每个结点至少有(m+1)/2个子节点。
(3) 如果根节点不为空,根结点至少有两个子节点。
(4) 所有叶子结点增加一个链指针,所有关键字都在叶子结点出现。
(5) 除了叶节点,结点的孩子数目等于关键字数目。
(6) B+树非叶子结点不保存关键字记录的指针,只进行数据索引;
叶子节点保存了父节点的所有关键字记录的指针,所以所有数据地址必须要到叶子节点才能获取到
我们从条件就可以看出,通过只在叶子节点上保存指针地址的结构,B+树每个非叶子节点存储的关键字数更多,树的层级更少所以相比B树而说,查询数据更快而且更加稳定。
B+树天然具备排序功能:B+树所有的叶子节点数据构成了一个有序链表,在查询大小区间的数据时候更方便,数据紧密性很高,缓存的命中率也会比B树高。通过这个特点,为了sql的命中率问题,选择了B+树而非B树。
我们来看看B+树的结构:
可以发现,B+树的上面和普通的B树没有什么区别但是子树的第一个值是上面的对应的值,比如1对下来的子树的第一位还是1.原因就在于我们没有办法直接通过1如获得对应的值的地址,我们必须遍历到也节点才能拿到对应的值。
然后我们可以看到,叶子结点个结构就是一个有序的链表,所以我们可以通过有序链表的遍历很快的找到对应值,并且由于B+树自平衡的特性,每一次的查找树高都是一样的,所以B+树的查询是稳定的O(logN)
了解了B+树,我们谈谈为什么说到数据库就离不开B+树:
B树和B+树是很适合磁盘读取的数据结构,由于存储介质的特性,我们为了提高磁盘使用效率,需要尽量的减少磁盘的I/O,所以一般而言,磁盘并不是按需读取的。而是每次读取的同事都会预读取一定长度的数据放入内存。
根据局部性原理:
当一个数据被用到时,其附近的数据也通常会马上被使用。
程序运行期间所需要的数据通常比较集中。
由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率。
预读的长度一般为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页得大小通常为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,然后异常返回,程序继续运行。
所以我们就可以很清晰的明白数据库索引的几大特性的由来了:
范围查询:比如要查主键在[1,17]之间的记录。二次查询,先查找 1所在的叶子节点的记录位置,再查找17所在的叶子节点记录的位置 (就是16所处的位置),然后顺序地从1遍历链表直到16所在的位置。
前缀匹配模糊查询:假设主键是一个字符串类型,要查询where Key like abc%,其实可以转化成一个范围查询Key in [abc,abcz]。当然,如果是后缀匹配模糊查询,或者诸如where Key like %abc%这样的中间 匹配,则没有办法转化成范围查询,只能挨个遍历。
排序与分页:叶子节点天然是排序好的,支持排序和分页。分页就是预读取的page,一次I/O就是一个page。