BST,AVL树,红黑树,B树,B+树,B*树(从map的底层实现到mysql索引原理)

本文不会对具体细节过多的探究,力求得到这几种树的联系以及区别,实际运用。

BST(二叉检索树):

二叉检索树也是我们最熟悉的一个索引方式了,它保证所有节点的左子树都小于该节点,所有节点的右子树都大于该节点。就可以通过大小比较关系来进行快速的检索,在一棵满二叉平衡树的情况下,检索的效率可以达到logn(类似二分检索),然后插入和删除的效率也是稳定的logn。
还是上一张图吧:
《BST,AVL树,红黑树,B树,B+树,B*树(从map的底层实现到mysql索引原理)》
BST可以很多题目相关考察的重点,这里要注意:如何找BST的最低公开节点,BST的中序遍历得到的结果是递增序列。
这里要讲一下BST的一个缺点,由于BST没有相关措施保持平衡,很容易因为插入数据的不对导致树的不平衡,当极度不平衡的时候,BST就会退化成一个链表,搜索的速度自然也就降低到了N,相关维护的代价也高(要想插入和删除,前提是要找到该节点)。

AVL树:

AVL树解决了BST中容易出现不平衡的问题,通过旋转的手段来让BST保持满二叉树的性质,当插入一个新节点之后,从插入结点向上判断是否出现高度差超过2的结点,然后通过旋转来恢复平衡,具体的平衡规则可以看我这篇博客:
https://blog.csdn.net/github_33873969/article/details/79487199

AVL树有一个缺点就是维护的代价过高,相较于BST,AVL树要有一个保持平衡的旋转操作,并且要保持严格平衡,所以维护代码高。

红黑树:
红黑树是我这篇文章要说的一个重点,我们的stl中的map也是低层也是通过红黑树来实现的,先说下红黑树的5个基本原则吧:
红黑树首先是二叉平衡树

1.每个结点或是红色的,或是黑色的。
2.根结点是黑色的。
3.每个叶结点(NIL)是黑色的。
4.如果一个结点是红色的,则它的两个子结点都是黑色的。
5.对每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点。

重点关注一下原则4和5哈,这两个原则可以保证我们树的最长路径不超过最短路径的两遍,也就意味着一棵有n个内部结点的红黑树的高度至多为2lg(n+1),虽然不是严格平衡,但是它的查找效率还是符合我们的要求的。
接下来就来说下红黑树在插入和删除的时候,维护红黑树相关性质的代价如何:
首先,红黑树是二叉平衡树,找到该节点的代码是logn。
接下来是调整,关于插入情况,如图所示:

《BST,AVL树,红黑树,B树,B+树,B*树(从map的底层实现到mysql索引原理)》
插入的结点都为红色。
对于红黑树而言,插入的时候要保持原则4,考虑的重点在于叔结点的情况:
情况1:z的叔结点为红。
解决办法:改变父结点和叔结点的颜色为黑,祖父节点为红或者黑(看情况)
情况2: z的叔结点y是黑色的且z是一个右结点。
解决办法:对z的父节点进行左旋之后按照原则进行变色。
情况3:z的叔结点y是黑色的且z是一个左结点。
解决办法:对z的父节点进行右旋之后按照原则进行变色。
变化结果,可以从上图看出。
接下来说删除,删除树的节点的原则就是子树替换
还是先上图:
《BST,AVL树,红黑树,B树,B+树,B*树(从map的底层实现到mysql索引原理)》
关于删除的调整操作,我们就更关注的是兄弟节点的状态如何了:
删除结点后,替换上的结点是黑色结点
1.节点X的兄弟节点w为红色,这个时候对x的父结点进行左旋,之后进行变色的操作。
2.x的兄弟节点w是黑色的,而且w的两个子结点都是黑色的。
解决办法:在这个条件下,违反的是原则五,因为黑高不统一。w结点变红,父节点考虑变色。
3.x的兄弟结点w是黑色的,w的左孩子是红色的,w的右孩子是黑色的。
解决办法:对w结点进行右旋再进行变色。(节点c和d替换色)
4.x的兄弟结点w是黑色的,且w的右孩子是红色的
解决办法:对w结点进行左旋再进行变色(A,B,C三个结点变色)
其他镜像问题也可以按照这个来解决。
红黑树虽然没有AVL树一样高度平衡,但是对于搜索的效率维持在log的数量级也够了。
并且红黑树的维护效率比AVL树高得多,最多只需要三次旋转,并且变色操作的复杂度可以忽略。
所以我们的map的

B树

刚刚说了那么多有关红黑树的内容,说了map的《key,value》是通过key来进行排序得到的。然而,我们的数据库所存储的数据不也是《key,value》为什么不用红黑树做我们的实现呢?
原因是局部性原理和内存不足原因:对于上面的BST,AVL,红黑树可以看作是一个个链表相叠加而成的。但是,链表通过指针的方式连接下一个数据,导致数据并不是处于一段连续的空间,也就无法使用缓冲的性质(连续读取一个页的数据进来使用),数据的局部性不强。特别是针对大数据情况,类似链表的树实则不是一个很好的选择。
2.并且对于大数据量的(例如数据库)不可能把所有的数据都载入main memory,所以不使用上述的红黑树结构
那么,有没很好利用局部性原理的检索树呢?
当然是有的啦,我们先从最开始的B树讲起,先上图:
《BST,AVL树,红黑树,B树,B+树,B*树(从map的底层实现到mysql索引原理)》
也称B-树
每个结点的相关数据都是一个《key,value》的二元组。关于B树,这里有几个数据的相关定义:
1.树的度d>=1,就是每一层的一个结点的数据长度。
2.h为一个整数,表示一个B树的高度。
3.每个非叶子节点由n-1个key和n个指针组成,其中d<=n<=2d。
4.每个叶子节点最少包含一个key和两个指针,最多包含2d-1个key和2d个指针,叶节点的指针均为null 。
5.所有叶节点具有相同的深度,等于树高h。
6.key和指针互相间隔,节点两端是指针。
7.一个节点中的key从左到右非递减排列。
8.所有节点组成树结构。
9.每个指针要么为null,要么指向另外一个节点。
上图中表示的是一个d=2的B树,大家可以看到每一层的数据都是数字+指针+数字,在检索的时候符合局部性原理。
看了这个图,我相信的检索的方式也十分明了了:
每一层进行顺序检索,找到数字就返回。不然,找到对应的区间,到达下一个结点继续递归寻找,代码如下:

BTree_Search(node, key) {
    if(node == null) return null;
    foreach(node.key)
    {
        if(node.key[i] == key) return node.data[i];
            if(node.key[i] > key) return BTree_Search(point[i]->node);
    }
    return BTree_Search(point[i+1]->node);
}
data = BTree_Search(root, my_key);

从这些代码可以看出,B树很好的使用了数据的局部性原理,并且树高得到了控制,检索的效率以可以保持在logn的级别。但是,有两个缺点:
1.维护的代价依旧高,对于B树就不再是旋转以及变色的操作,而是进行分裂(插入结点,多了就分裂)和合并(删除结点,少了就合并)的操作。
2.我们的数据依旧在主存里面,一旦数据量一多,就无法把数据库中的数据全放主存当中。

B+树

出现了问题,自然就有解决的办法。
我们的B+树就诞生了,我们mysql的索引的底层实现也是基于B+树而实现的:
最重要的区别是:我们的数据现在都被存在了叶子结点,索引在非叶结点上。
形状如下:
《BST,AVL树,红黑树,B树,B+树,B*树(从map的底层实现到mysql索引原理)》
我们就可以在检索的时候不把数据库中的大量数据载入(从磁盘载入:磁盘载入的效率非常慢:不旦IO慢,而且寻道时间也慢)了,而是要找到结果的时候才把数据载入,非常我们数据库的数据。实际上,我们的mysql的索引的底层实现也是基于B+树而实现的。
这里介绍一个数据库引擎:
MyISAM:
B+树的叶子结点存储的是对应检索条的地址:

《BST,AVL树,红黑树,B树,B+树,B*树(从map的底层实现到mysql索引原理)》
地址指向我们的数据条。这就实现了我们的索引与数据分开的非聚集索引。
这里有个特点,每个叶子结点都可以通过指针相连,这就支持我们进行顺序打印了。

有非聚集索引自然也有聚集索引:
这里介绍的InnoDB主索引就是聚集索引,当实际数据跟索引相结合,如下图所示:
《BST,AVL树,红黑树,B树,B+树,B*树(从map的底层实现到mysql索引原理)》
所以,聚集索引的InnoDB是必须设置主键的。
竟然必须设置主键,那么如果能设置一个递增的数据列作为主键就有利于改善检索效率(减少Page fault 即page in 和page out)。
非聚集索引则不一定设置主键。

是否采取索引的条件主要在于两个方面:
1.数据量是否大(2000条为线)
2.数据的选择性(重复的条数多少),重复的条数越少越适合索引。(每一条索引的返回值不能太多)

B*树

《BST,AVL树,红黑树,B树,B+树,B*树(从map的底层实现到mysql索引原理)》

在B+树的基础上把叶子结点的数据用指针相连,并且在相同层的不同树节点也用指针相连
Reference:http://blog.codinglabs.org/articles/theory-of-mysql-index.html
《算法导论》
http://www.cnblogs.com/daoluanxiaozi/p/3340382.html
http://daoluan.net/学习总结/数据结构/算法/2013/09/28/rbtree-is-not-difficult-2.html
https://blog.csdn.net/qq_17612199/article/details/50944413

    原文作者:B树
    原文地址: https://blog.csdn.net/github_33873969/article/details/79677331
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞