MySQL 简单了解B+树

前言

本人刚刚开始了解数据结构,所以本篇文章只讲了关于以下目录中数据结构的定义概念,这里不涉及在树中更改数据时,对树结构进行翻转操作及其他复杂情况的说明,以后系统学习数据结构的时候会重新总结,再记录一下关于数据结构的系列笔记。

数据结构练习网站 Data Structure Visualization


一、二叉树

定义:即每个节点都最多只有两个子节点的树。

以下图片中数字仅代表节点位置,不代表节点内容。

根据二叉树的节点分布大概可以分为以下三种二叉树:满二叉树完全二叉树平衡二叉树

1、特殊类型

⑴、满二叉树

从形象上来说满二叉树是一个绝对的三角形,也就是说它的最后一层全部是叶子节点,其余各层全部是非叶子节点,即都有左右子节点,如果用数学公式表示,k表示深度,也就是层数,那么其第 k 层的节点数就是 n=2k-1 。也就是说满二叉树的节点数是一系列固定的数,比如说,1,3,7,15…如果节点数不是这个序列中的数,那么他肯定不是满二叉树,当然了,反之,是不成立的。

⑵、完全二叉树

完全二叉树用数学公式来讲就是对于 k 层的完全二叉树,其1~ k-1层为满节点,且其 k 层的节点数的范围是 2(k-2) < N < 2k-1;

完全二叉树和满二叉树的区别是,他的最后一行可能不是完整的,从形状上来说他是一个可能有缺失的三角形,但绝对是右方的连续部分缺失。

2、什么是二叉查找树?

⑴、二分查找算法

二分查找(binary search) ,又叫 折半查找二分搜索二分查找的使用,要有一个前提条件:要查找的数必须在一个有序数组里。在这个前提下,取中间位置数作为比较对象:

  • 若要查找的值和中间数相等,则查找成功。
  • 若小于中间数,则在中间位置的左半区继续查找。
  • 若大于中间数,则在中间位置的右半区继续查找。

不断重复上述过程,直到查找成功或者查找区域变为 0,查找失败。

⑵、二叉查找树(BST)

二叉查找树(BST:Binary Search Tree)也称为 二叉搜索树 、二分搜索树、有序二叉树或二叉排序树。就是实现二分查找算法的二叉树。

二叉查找树(BST)可以是一颗空树,或者具有以下性质的二叉树。

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值。
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值。
  • 它的左右子树也分别为二叉搜索树。

一般情况下 二分查找 比顺序查找在时间上要快很多,但是在频繁修改的表中采用 二分查找,其效率是非常低下的,因为顺序表的修改操作效率低下,而二分查找的高效就是利用顺序表的索引来取值进行比较的。为了支持频繁的修改,我们需要采用链表这种数据结构。然而,单链表的查找效率又非常低,为了解决这一问题,二叉查找树(BST) 便诞生了。

如下最左边的图,最好的情况是一个完全二叉树,这样查找效率最高。

如下最右边的图,最坏的情况会退化成一个链表,这样查找效率最低。

下表时间复杂度是根据已知数值,对二叉搜索树进行操作的总结:

数据结构增加删除修改查找
二叉树(最好情况)O(logn)O(logn)O(logn)O(logn)
二叉树(一般情况)O(logn)~O(n)O(logn)~O(n)O(logn)~O(n)O(logn)~O(n)
二叉树(最坏情况)O(n)O(n)O(n)O(n)

注:树的高度和节点的关系就是以2为底,树的节点总数n的对数

3、自平衡二叉查找树

⑴、平衡二叉树(AVL)

平衡二叉树,又称 AVL树,用于解决二叉排序树高度不确定的情况,如果二叉排序树的子树间的高度相差太大,就会让二叉排序树操作的时间复杂度升级为O(n),为了避免这一情况,就出现了平衡二叉树,使树的高度尽可能的小,其本质上是带了平衡功能的二叉查找树(二叉排序树,二叉搜索树)。

平衡二叉树的性质:

  • 左子树和右子树的高度之差的绝对值小于等于1
  • 左子树和右子树也是平衡二叉树
平衡因子(BF)= 节左子树的高度 - 节右子树的高度。

因此平衡二叉树所有节的平衡因子只能是-1、0、1,如下左图,是一个平衡二叉树,下图中红色数字为节的平衡因子(BF):

失衡: 当我们在一个平衡二叉树上插入一个节点时,有可能会导致失衡,即出现平衡因子绝对值大于1 的节点,当插入节点后,其中其上6号节点失去了平衡,我们称该节点为 失衡节点 ,我们必须重新调整树的结构,使之恢复平衡。

关于平衡二叉树的 失衡恢复平衡 这里不做多说,可自行了解。

⑵、红黑树

通过上面的图片还有表格可以知道二叉搜索树的时间复杂度介于O(logN)和O(N)之间,而为了应对极端情况,红黑树就出现了,它是具备了某些特性的二叉搜索树,能解决非平衡树问题,红黑树是一种接近平衡的二叉树(说它是接近平衡因为它并没有像AVL树的平衡因子的概念,它只是靠着满足红黑节点的5条性质来维持一种接近平衡的结构,进而提升整体的性能,并没有严格的卡定某个平衡因子来维持绝对平衡)。

首先红黑树是一个二叉搜索树,其次红黑树的每个节点都带有颜色属性的二叉查找树,颜色是红色或黑色。对于任何有效的红黑树我们增加了如下的额外要求:

  • 1、节点是红色或黑色。
  • 2、根节点是黑色。
  • 3、所有 NIL 叶子节点都是黑色,这里的叶子节点指的是最底层的空节点(外部节点)。
  • 4、每个红色节点的两个子节点都是黑色。
  • 5、从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

由以上特性会出现这些情况:

  • 特性4导致从每个叶子节点到根节点的路径上不能有两个连续的红色节点。
  • 性质5导致没有路径能多于任何其他路径的两倍长。
    • 最短路径:都是黑色节点;最长路径:一黑一红,交替出现;所以最长路径刚好是最短路径的2倍。
  • 默认新插入的节点为红色:将节点设置为红色在插入时对红黑树造成的影响是小的,而黑色是最大的。
    • 假如任意插入的节点是黑色节点,则连续插入两个黑色节点后,就不满足性质5(肯定有一边的黑色节点多于另外一边);

这些约束强制了红黑树的关键性质,结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。

数据结构\复杂度增加删除修改查找
二叉搜索树(一般情况)O(logn)~O(n)O(logn)~O(n)O(logn)~O(n)O(logn)~O(n)
平衡二叉树O(logn)O(logn)O(logn)O(logn)
红黑树O(logn)O(logn)O(logn)O(logn)

由于平衡二叉树和红黑树都是自平衡的二叉搜索树。平衡二叉树和红黑树分别由平衡因子和五个特性来控制平衡,所以他们的效率都是高效的。

⑶、AVL树 vs 红黑树

AVL树:

  • 平衡标准比较严格:每个左右子树的高度差不超过1

  • 最大高度是 1.44 ∗ log2 n + 2 − 1.328(100W个节点,AVL树最大树高28)
    搜索、添加、删除都是 O(logn) 复杂度,其中添加仅需 O(1) 次旋转调整、删除最多需要 O(logn) 次旋转调整
     

红黑树:

  • 平衡标准比较宽松:没有一条路径会大于其他路径的2倍

  • 最大高度是 2 ∗ log2(n + 1)( 100W个节点,红黑树最大树高40)

  • 搜索、添加、删除都是 O(logn) 复杂度,其中添加、删除都仅需 O(1) 次旋转调整

 
如何选择:

  • 搜索的次数远远大于插入和删除,选择AVL树;搜索、插入、删除次数几乎差不多,选择红黑树

  • 相对于AVL树来说,红黑树牺牲了部分平衡性以换取插入/删除操作时少量的旋转操作,整体来说性能要优于AVL树

  • 红黑树的平均统计性能优于AVL树,实际应用中更多选择使用红黑树

参考:【数据结构】史上最好理解的红黑树讲解,让你彻底搞懂红黑树

二、B树

树家族是为了磁盘或其它存储设备而设计的一种平衡多路查找树。树上操作的时间通常由存取磁盘的时间和CPU计算时间这两部分构成,而CPU的速度非常快,所以B树的操作效率取决于访问磁盘的次数,关键字总数相同的情况下B树的高度越小,磁盘I/O所花的时间越少。

树的高度是命中查找的一个不可抗拒的时间下限。在一定的数据条件下,树的高度和宽度是互相制约的。(就像一定面积下,矩形的长和宽是互相制约的)而树家族中最简单的二叉树,尽管易于实现,却不能有实际的价值。其最最令人发指的是二叉树的高度太高。多叉树的提出和实现解决了二叉树的不足。

1、B-树

B-树就是B树,它是一种平衡的多叉树,不是B减树,而是B杠树,中文通常称为B树,英语称为B-tree。

由上面的平衡二叉树演变成平衡多叉查找树,依然保持基本规则:每个关键字的左侧子树与右侧子树的高度差的绝对值不超过1的查找树,用以解决树的高度问题。而B树限制更强,要求所有叶子节点都在同一层。

 
一棵 M 阶的B树满足下列性质:

  • 1、根节点至少有两个子树,子树数量的范围是 2 ≤ 根节点子树数 ≤ M
  • 2、每个非根节点所包含的关键字个数满足: ceil(M/2) ≤ 关键字个数 ≤ M-1,其子树数为关键字个数加一,且这些关键字按照递增顺序排列;
  • 3、除根节点以外的所有节点(不包括叶子节点)的度数正好是关键字总数加1,故内部子树个数满足: ceil(M/2) ≤ 子树个数 ≤ M
  • 4、所有的叶子节点都位于同一层。

B树每个节点的结构都包含关键字个数n+1个指针n个关键字,指针指向子树位置,叶子节点中的指针全部用空指针表示,是查找失败到达的位置:

下面是一个B树结构图,数据是存在每个节点上的:

现在需要去查找关键字28,那么就需要3次磁盘IO,如果数据量越大,树的高度越高就会导致磁盘IO次数越多。

结构源码:

#define  m  3       // B树的阶,此设为3
typedef int KeyType;
 
typedef struct {
  KeyType  key;
  char     data;
} Record;
 
typedef struct BTNode {
  int             keynum;        // 节点中关键字个数,即节点的大小
  struct BTNode  *parent;        // 指向双亲节点
  KeyType         key[m+1];      // 关键字向量,0号单元未用
  struct BTNode  *ptr[m+1];      // 子树指针向量
  Record         *recptr[m+1];   // 记录指针向量,0号单元未用
} BTNode, *BTree;                // B树节点和B树的类型
 
typedef struct {
  BTree    pt;      // 指向找到的节点
  int      i;       // 1..m,在节点中的关键字序号
  int      tag;     // 1:查找成功,0:查找失败
} Result;           // 在B树的查找结果类型

2、B+树

B+树是B树的一种变形形式,B+树上的叶子节点存储关键字以及相应记录的地址,叶子节点以上各层作为索引使用。

一棵 M 阶的B+树定义如下:

  • 1、每个节点至多有 M 个子树;
  • 2、除根节点外,每个节点有 ceil(M/2)M 个子树,根节点至少有两个子树;
  • 3、有 k 个子树的节点必有 k 个关键字。
  • 4、叶子节点包含了全部关键字和数据指针,叶子节点内的关键字有序排列,叶子节点间也是有序排列,指针相连。
  • 5、所有非叶子节点可以看成是索引,仅包含其子树中最大(或最小)关键字的值。
  • 6、所有的叶子节点都位于同一层。

B+树的查找与B树不同,当索引部分某个节点的关键字与所查的关键字相等时,并不停止查找,应继续沿着这个关键字左边的指针向下,一直查到该关键字所在的叶子节点为止。

由于B+树非叶子节点不存放数据地址,那这部分多出来的空间可以存放更多的关键字,这样一来B+树的高度也会比B树低,磁盘IO次数会更少。

以下是一个B+树结构图:

不过这里我有一个疑惑的地方,在 Data Structure Visualization 上测试所显示的结构并没有按照特性3显示,我不知道是不是 Data Structure Visualization 上显示问题。

3、B*树

这里直接转载百度百科。

B树是B+树的变体,在B+树的非根和非叶子节点再增加指向兄弟的指针;B树定义了非叶子节点关键字个数至少为(2/3)*M,即块的最低使用率为2/3(代替B+树的1/2)。

B+树的分裂:当一个节点满时,分配一个新的节点,并将原节点中1/2的数据复制到新节点,最后在父节点中增加新节点的指针;B+树的分裂只影响原节点和父节点,而不会影响兄弟节点,所以它不需要指向兄弟的指针;

B*树的分裂:当一个节点满时,如果它的下一个兄弟节点未满,那么将一部分数据移到兄弟节点中,再在原节点插入关键字,最后修改父节点中兄弟节点的关键字(因为兄弟节点的关键字范围改变了);如果兄弟也满了,则在原节点与兄弟节点之间增加新节点,并各复制1/3的数据到新节点,最后在父节点增加新节点的指针;

B*树分配新节点的概率比B+树要低,空间使用率更高;

4、B树和B+树简单对比

B+树和B树相比的主要区别:

  • 1、就是B+树所有关键码都在叶子节点,树的高度矮,因为B+树存储量很大的层级一般在2–4层,经行磁盘io的次数和层数相同。

  • 2、B+树的叶子节点是带有指针的,且叶节点本身按关键码从小到大顺序连接。

  • 3、在搜索过程中,如果查询和内部节点的关键字一致,那么搜索过程不停止,而是继续向下搜索这个分支

 
为什么InnoDB选择B+树而不是B树:

  • 1、B+树的磁盘读取代价低, B树每个节点都有data,B+树只有叶子节才有,假设每个节点大小16KB,那么B+树比B树能存储更多的关键字,一次性读入内存的关键字的内存也会更多,B+树的高度也会比B树低,磁盘IO次数会更少。

  • 2、B+树对范围查询更友好,方便遍历,B树叶子节点没有链接,而B+树叶子节点通过双向指针链接,可以很方便的进行范围查询,比如where条件中 age >= 3 and age < 20,那么当找到3时就可以顺着指针找到20,而B树是不可以的。

  • 3、B+树查询效率稳定性更好, 在B+树中,由于分支节点并不是最终指向文件内容的节点,分支节点只是叶子节点的索引,所以对于任意关键字的查找都必须从根节点走到分支节点,所有关键字查询路径长度相同,每个数据查询效率相当。而对于B树而言,其分支节点上也保存有数据,对于每一个数据的查询所走的路径长度是不一样的,效率也不一样,B树稳定性不如B+树好

三、AVL树,红黑树,B树,B+树都分别应用在哪些现实场景中

  • AVL树: 平衡二叉树,一般是用平衡因子差值决定并通过旋转来实现,左右子树树高差不超过1,那么和红黑树比较它是严格的平衡二叉树,平衡条件非常严格(树高差只有1),只要插入或删除不满足上面的条件就要通过旋转来保持平衡。由于旋转是非常耗费时间的。我们可以推出AVL树适合用于插入删除次数比较少,但查找多的情况。

    • 应用相对其他数据结构比较少。windows对进程地址空间的管理用到了AVL树。
       
  • 红黑树: 平衡二叉树,通过对任何一条从根到叶子的简单路径上各个节点的颜色进行约束,确保没有一条路径会比其他路径长2倍,因而是近似平衡的。所以相对于严格要求平衡的AVL树来说,它的旋转保持平衡次数较少。用于搜索时,插入删除次数多的情况下我们就用红黑树来取代AVL。红黑树应用比较广泛:

    • 广泛用在C++的STL中。map和set都是用红黑树实现的。
    • 著名的linux进程调度Completely Fair Scheduler,用红黑树管理进程控制块。
    • epoll在内核中的实现,用红黑树管理事件块
    • nginx中,用红黑树管理timer等
    • Java的TreeMap实现
  • B树,B+树: 它们特点是一样的,是多路查找树,一般用于数据库中做索引,因为它们分支多层数少,因为磁盘IO是非常耗时的,而像大量数据存储在磁盘中所以我们要有效的减少磁盘IO次数避免磁盘频繁的查找。

    • B+树是B树的变种树,有n棵子树的节点中含有n个关键字,每个关键字不保存数据,只用来索引,数据都保存在叶子节点。用在文件系统数据索引

    • B+树相对B树磁盘读写代价更低:因为B+树非叶子结点只存储键值,单个节点占空间小,索引块能够存储更多的节点,从磁盘读索引时所需的索引块更少,所以索引查找时I/O次数较B-Tree索引少,效率更高。而且B+Tree在叶子节点存放的记录以链表的形式链接,范围查找或遍历效率更高。MySQL InnoDB用的就是B+Tree索引

参考:AVL树,红黑树,B树,B+树,Trie树应用场景简介

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值