多叉平衡搜索树
多叉平衡搜索树:包括B树,B+树 ,B*树
也就是说一个结点可以有多个孩子
二叉平衡搜索树 和 多叉平衡搜索树
有了二叉搜索树的情况下,为什么还要研究多叉平衡搜索树的问题?
二叉树的查找过程是看作从根节点走叶子的每一天路径,相比较而言,树的高度越低,意味着,访问结点的次数更少。(每向下走一层,需要读取一次结点)
多叉搜索树的特点,在访问磁盘数据(其他持久化的设备)数据时,最适合。
当数据存放在磁盘上时,需要搜索相关数据结构时,优先考虑使用B-树系列,而不是二叉搜索树(AVL/红黑树)
B-树的数据结构
(由于飞哥说好像没有B-树这个东西 ,再次经过查阅负责任的说 B树就是 B-树 ,因为B树的原英文名称为B-tree,而国内很多人喜欢把B-tree译作B-树 -只是一个连接符 千万那个别叫B减树就好!)
下面的这个树,可以称作3-4树(三个结点,至多四个孩子,可以存在空)或者也叫做4树
B树也可以叫做M-树,表示的每个结点最多有几个孩子(这也就意味着有M-1个key)
阶数 表示 此树的结点 最多 有 多少个孩子结点(子树),一般用字母 M 表示阶数。
- key不可以重复
- key1 、2 、3要按照大小关系排布
结点构造
3-4 树为例
查找
和搜索树一样的方式
插入
按照以下的规则进行插入以后,要保证一点,B-树的所有叶子结点都在同一层!
B-树生长是向上生长的!
- B树的插入,严格按照先查找,再插入的过程
- B树的插入,一定发生再叶子结点上(第一次插入需要创建新结点)
- B树(以3-4树为例)最多只允许存在3个结点,如果出现超过3个key的情况需要通过结点分类的方式进行修复
下面进行一个B树的插入演示和分裂的过程
以下面的数字为例:12 35 39 2 87 10 70 3 98 52 5 84 94 14 26 37
绘制3-4树为例子:
首先创建根节点 按顺序放入12 35 39
当要插入2 的时候 发现超过了3个key,按顺序排列后 为 2 12 35 39 ,对结点进行分裂。选取中间树 12 放到父节点中,由于没有父节点,所以创建一个父节点。同时 35 39 也要放入新的结点中,只有2 依旧在原始的A结点中
再依次在叶子结点中插入10 、 87 之后要插入70 又会引起分裂。
接着插入3 98 当要插入52的时候 又会在一起引起结点的分裂。
52插入后,插入5再次分裂结点。
3 应该到父节点去,但是由于父节点也满了,所以对父节点进行再次的分裂。
对根分裂后,可以很明显的看出来树是一步步往上生长的。
接着插入 84然后插入 94导致结点分裂,在插入14 26 之后如图 。但是当再次插入37知州 又依次导致结点的分裂。同时引起根节点的分裂。
最终B-树的形态如图。
一个B树插入的可视化网站 这个网站可以详细到展现结点的分裂过程,十分清晰!
特点总结
- B-树的高度是由于根分裂才会生长
- B-树的所有叶子都在同一层 (也就是说所有子树的高度一致)
由于所有叶子都在同一层,所以B-树是一颗平衡树,进而B-树是多叉平衡搜索树。
B树相对于平衡二叉树的不同是,每个节点包含的关键字增多了。(磁盘数据存储是采用块的形式存储的,每个块的大小为4K,每次IO进行数据读取时,同一个磁盘块的数据可以一次性读取出来)把节点大小限制和充分使用在磁盘快大小范围;把树的节点关键字增多后树的层级比原来的二叉树少了,减少数据查找的次数和复杂度;)
每一次IO读取的数据我们称之为一页(page),具体一页有多大数据跟操作系统有关,一般为4k或8k,也就是我们读取一页内的数据时候,实际上才发生了一次IO。
上图3叉(只是举例,真实会有很多叉)的BTree结构图,每一个方框块我们称之为一个磁盘块或者叫做一个block块,这是操作系统一次IO往内存中读的内容,一个块对应四个扇区,紫色代表的是磁盘块中的数据key,黄色代表的是数据data,蓝色代表的是指针p,指向下一个磁盘块的位置。
参考
但是也会有一些问题产生:
每个节点中不仅包含数据的key值,还有data值。而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。
B+树
B+树是B树的一个升级版,相对于B树来说B+树更充分的利用了节点的空间,让查询速度更加稳定。
结点信息
- 内部结点(也称索引结点)非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度。
- 叶子结点。内部节点就是非叶子节点
- 内部节点不存储数据,只存储索引,数据都存储在叶子节点
- 每一个父节点的元素都出现在子结点中。(是子节点的最大或者最小的元素)
插入
当节点元素数量大于m-1的时候,按中间元素分裂成左右两部分,中间元素分裂到父节点当做索引存储,但是,本身中间元素还是会分裂在 右边这一部分的。
参考链接
下面以一颗5阶B+树的插入过程为例,当一个结点的元素个数大于4的时候 需要分裂。
特点比较
MySQL 的数据也是存储在磁盘上的,而且MySQL 提供的最重要的功能就是查找的功能。MySQL 内部要进行数据在磁盘上的组织管理,也是需要B-树,(B+树)进行维护。
比较 | B-树 | B+树 |
---|---|---|
数据存储 | 每个结点都会存储key - value | 内部节点不存储数据,只存储索引。所有的key - value 数字 全部在叶子上保存起来 |
数据重复 | 数据不会重复存储 | 数据重复 |
关键字 key 和结点的数量 | B树分支结点k个 则存在 k-1 个关键码 | B+树 分支结点(不是根结点哈!)M个关键字,叶子节点也必须有M个 |
叶子结点组织方式 | 叶子结点都在同一层 | 除在同一层之外,链表组织叶子结点(可以单向也可以双向) |
查找性能 | 获取key - value 时间复杂度不稳定 情况好一次就可以找到 不好的情况就是到叶子结点 | 时间复杂度稳定查找性能稳定 必须要到叶子结点才可以获取到 |
范围查询 | 范围查询复杂 | 所有叶子节点形成有序链表,便于范围查询。 |
IO | ~ | 相比较而言IO的次数更少(由于结点的存放信息多) |
优点 | ~ | 遍历所有的key - value 变得容易(进行链表的遍历即可) |
缺点 | 元素key 不重复 相比较 空间少 | 空间使用更多 会重复的存储key |
共同点 | 平衡二叉搜索树 | ~ |
B+ 树在数据库中的索引
数据库中的B+Tree索引可以分为
- 聚集索引(clustered index)
- 辅助索引(secondary index)。
上面的B+Tree示例图在数据库中的实现即为聚集索引
聚集索引的B+Tree中的叶子节点存放的是整张表的行记录数据。
辅助索引与聚集索引的区别在于辅助索引的叶子节点并不包含行记录的全部数据,而是存储相应行数据的聚集索引键,即主键。
当通过辅助索引来查询数据时,InnoDB存储引擎会遍历辅助索引找到主键,然后再通过主键在聚集索引中找到完整的行记录数据
补充 关于聚集索引和非聚集索引: