7.13.B+树

发布于:2025-08-04 ⋅ 阅读:(13) ⋅ 点赞:(0)

一.前言:

如上图,

上述图片的树是一个B+树,观察其特征。

如上图,

会发现B+树与分块查找有些类似,在分块查找中会把数据元素分为一块一块的,在索引表中会保存每一块中最大的关键字,现在再看刚才的B+树,第三层中,从左往右,第一部分里最大的关键字为3,第二部分里最大的关键字为9,第三部分里最大的关键字为15,刚好与第二层中从左往右的第一个结点的数据对应上,以此类推。


二.B+树的定义:

以上述图片的4阶B+树为例->

Ⅰ.特性1解读:

上述图片的B+树是一个4阶的B+树,4阶的意思是说每一个分支结点最多有4棵子树(注:分支结点指的是除了叶子结点外的结点)。

Ⅱ.特性2解读:

非叶根结点是什么呢?首先要明确的是在B+树中一般会把最下面一层的结点称为叶子结点,所以非叶根结点就是不是叶子结点的根结点,

如下图:

如上图,

  1. 上述图片最左边的B+树中只有一个结点即根结点,这个结点下方已经没有其他的结点了,所以这个根结点同时也是叶子结点,此时该根结点同时也是叶子结点,所以与特性2无关

  2. 上述图片最右边的B+树根结点并不是最下层的结点,所以该根结点并不是叶子结点,因此当根结点不是叶子结点时,那么根结点至少有2棵子树

  3. 上述图片最中间的树,该树的根结点(即蓝色结点)只有左子树,没有右子树,并不符合B+树特性2的定义

非叶根结点为什么要规定至少有两棵子树呢?其思想与B树的一致,就是希望B+树的高度尽可能地低,以追求"绝对地平衡",也就是说对于任何一个结点,它的所有子树的高度都要保证相同->所以本质上就是为了追求平衡,因此才有非叶根结点至少有两棵子树。

对于一个4阶B+树,除了非叶根结点外,其他每个分支结点至少有⌈4/2⌉=2棵子树,至于为什么这么规定,原理类似B树,具体是因为要保证每一个结点不要太空,因为每一个结点的子树的数量太少的话,就会导致B+树整体上来看会很高,这样会降低查找效率。

Ⅲ.特性3解读:选择题常考,因为这是B+树与B树之间最大的区别

以本例的4阶B+树为例,

第二层中从左往右数第一个结点有3个关键字,那么这3个关键字会对应3个分支即3棵子树,所以B+树中一个结点中含有几个关键字,对应的就会有几个分支(子树)->在B树中如果一个结点有2个关键字,那么这个结点会有3个分支(详情见"7.11.B树(又名B-树)")。

Ⅳ.特性4解读:

以本例的4阶B+树为例,

假设该B+树里存储的是每一个学生的学生信息,那么该B+树的叶子结点里存储的就是学生的学号,

如果要找到一个学生的信息,就需要通过学生的学号找到某一个叶子结点,然后在该叶子结点中找到这个学生的学号所对应的数据项(有关数据项,详情见"1.1.数据结构绪论"),这个数据项包含了学生的学号的关键字,并且其中包含了指向这个学生实际信息的指针,也就是上述图片中的"记录"(灰色框),

这样的场景中每一个学号就是一个关键字,而所有的关键字都会出现在最下层的叶子结点中,并且会把叶子结点中的数据按照关键字的大小顺序进行排列,然后会用指针把他们链接起来,

所以在B+树中其实可以通过上述图片里的指针p,然后从第一个叶子结点开始,一个一个往后遍历,把所有的叶子结点都遍历完,也就是说B+树支持顺序查找。

注:m阶B+树中所谓的叶子结点指的是一整块结点,而不是结点中的某一个关键字,一个叶子结点最多会包含m个关键字,叶子结点中还会包含指向相应记录的指针。

Ⅴ.特性5解读:

以本例的4阶B+树为例,

B+树的所有分支结点即除了叶子结点外的结点中仅包含它的各个子结点中关键字的最大值以及指向其子结点的指针,类似分块查找(有关分块查找,详情见"7.4.分块查找")。


三.B+树的查找:从树的根结点出发开始查找

1.查找成功:

以上述图片的B+树为例,

假设该B+树存储的是学生的学号,现在要查找编号为9的学生信息即查找关键字为9的数据,那么该如何查找呢?

首先从根结点出发,根结点包含的关键字依次为15、56,一开始指针指向根结点的第一个关键字15,

目标关键字9小于15,15表示的是它所指向的一整块分块中最大的关键字为15,所以如果目标关键字9存在,那么只可能在15所指的分块内,

因此需要让指针指向关键字15对应的下一级结点,

如下图:

如上图,

当前指针指向树的第二层从左往右数第一个结点中,该结点包含的关键字依次为3、9、15,

3小于目标关键字9,所以会让指针后移,

如下图:

如上图,

当前指针指向的关键字9等于目标关键字9,

  • 注:在B+树中如果只是在分支结点即除了叶子结点外的结点中找到了目标关键字,此时的查找并没有结束,因为目标关键字的具体信息到底存放在哪儿,只有找到最下面的叶子结点之后才可以得知(关键要找"灰色框"的记录)

此时指针无需后移,直接让指针指向关键字9对应的下一级结点,

如下图:

如上图,

此时指针指向了叶子结点,该叶子结点中的关键字依次为6、8、9,从左往右依次对比该叶子结点中的关键字,最终找到了目标关键字9,

如下图:

如上图,

通过当前指针所指的这一项里的数据信息,就可以找到目标关键字9即9号学生的详细信息具体存放的位置,也就可以得知9号学生的信息,意味着可以读出相应的记录。

2.查找失败:

以上述图片的B+树为例,

假设现在要查找关键字为7的数据,那么该如何查找呢?

首先从根结点出发,根结点包含的关键字依次为15、56,一开始指针指向根结点的第一个关键字15,

目标关键字7小于15,15表示的是它所指向的一整块分块中最大的关键字为15,所以如果目标关键字7存在,那么只可能在15所指的分块内,

因此需要让指针指向关键字15对应的下一级结点,

如下图:

如上图,

当前指针指向树的第二层从左往右数第一个结点中,该结点包含的关键字依次为3、9、15,

3小于目标关键字7,所以会让指针后移,

如下图:

如上图,

当前指针指向树的第二层从左往右数第一个结点中的第二个关键字9,

目标关键字7小于9,9表示的是它所指向的一整块分块中最大的关键字为9,所以如果目标关键字7存在,那么只可能在9所指的分块内,

因此需要让指针指向关键字9对应的下一级结点,

如下图:

如上图,

此时指针指向了叶子结点,该叶子结点中的关键字依次为6、8、9,

第一个关键字6小于目标关键字7,因此需要把指针后移;

第二个关键字8大于目标关键字7,由于此时指针已经指向叶子结点,所以到这儿如果没有找到目标关键字7,就说明目标关键字7不存在,

至此,查找失败,

如下图:

如上图,

由此可以得出:在B+树中,无论查找成功与否,最终一定都要走到最下面一层结点即叶子结点中才可以确定到底是查找成功还是查找失败,因为上面的分支结点即非叶子结点的信息并不能真实的反映出某一个关键字是否存在,并且只有找到最下面的叶子结点之后才可以找到某一个关键字实际对应的"记录"的存放位置。

3.对比:B树的查找

相比于B+树,在B树里查找有可能停在中间的某一层,

如下图:

以上述图片的5阶B树为例,该树共有三层,

比如查找关键字11,查找到树的第二层从左往右数第一个结点中就可以确定关键字11是存在的,

至此,查找成功,意味着查找结束。


四.B+树的查找:顺序查找

以上述图片的B+树为例,

除了从根结点开始逐层往下查找外,还可以通过上图中保存的指针p进行顺序查找,指针p直接从叶子结点出发,

此时要查找目标关键字9,

首先指针p指向关键字1,关键字1小于目标关键字9,因此指针p需要后移;

接下来指针p指向关键字3,关键字3小于目标关键字9,因此指针p需要后移;

接下来指针p指向关键字6,关键字6小于目标关键字9,因此指针p需要后移;

接下来指针p指向关键字8,关键字8小于目标关键字9,因此指针p需要后移;

接下来指针p指向关键字9,关键字9等于目标关键字9,因此就找到了目标关键字9,同时也可以找到目标关键字9对应的"记录"实际存放的位置。


五.B+树 VS B树:

B+树有很多地方类似分块查找与B树,考研中通常把B+树与B树进行对比的考察,现在介绍B+树与B树的区别->

Ⅰ.区别一:结点的子树(分叉)个数

如上图,

在B+树中,一个结点内n个关键字对应n棵子树(分叉);

在B树中,一个结点内n个关键字对应n+1棵子树(分叉),

如下图:

Ⅱ.区别二:结点的关键字个数

如上图,

在m阶B+树中,对于所有结点,每一个结点的分支树最多有m个,因此每一个结点最多有m个关键字;除了根结点外,每一个结点的分支树最少有⌈m/2⌉个,因此每一个结点最少有⌈m/2⌉个关键字;根结点中最少有1个关键字;

在m阶B树中,为了避免各个结点内的关键字个数太少,即为了保证整棵树不要太高,会给各个结点设定关键字的下限,或者是说给各个结点设定一个分支树的下限->除了根结点外,规定每一个结点的分支树至少为⌈m/2⌉个,意味着每一个结点至少要有⌈m/2⌉-1个关键字;对于根结点而言,根结点的关键字至少要有1个,至于为什么,详情见"7.11.B树(又名B-树)";对于所有结点,每一个结点的分支树最多有m个,意味着每一个结点最多有m-1个关键字(最下层的结点即终端结点的子树就是叶子结点,叶子结点实际不存在,所以不需要子树),

如下图:

Ⅲ.区别三:叶子结点

如上图,

在B+树中,叶子结点包含了全部的关键字,非叶子结点中出现过的关键字也会在叶子结点中出现,且每一个关键字有可能在多个结点中重复出现;

在B树中,各个结点中的关键字是不重复的,

如下图:

Ⅳ.区别四:查找方式

如上图,

在B+树中,只有叶子结点包含信息,所有的非叶子结点仅起索引作用,非叶子结点中的每个索引项只包含对应子树的最大关键字和指向该子树的指针,非叶子结点中并不包含该关键字对应记录的存储地址,以上图的4阶B+树为例,最下层的灰色框"记录"才是真实的数据信息,比如关键字15的数据信息到底存放在哪里,只有找到叶子结点中关键字15才可以确定,而非叶子结点即分支结点虽然也可以找到关键字15,但是这些分支结点只是起到索引即查找的作用,这些分支结点中并没有包含关键字15的实际信息到底存放在哪儿;

在B树中,每一个结点中既包含了关键字的信息,也包含了关键字对应的记录的存储地址,所以比如要在B树中找到关键字45,查找到第二层从左往右数第二个结点中的第二个关键字45的时候,就可以确定关键字45实际的数据存放的位置,所以这就是为什么在进行对B树的查找的时候只需要找到某一个结点中包含了所找关键字,就可以确定查找成功,意味着查找结束,而B+树中一定要查找到最下层的叶子结点中包含了所找关键字,此时才可以确定查找成功,这时就能理解B+树比起B树到底优秀在哪儿,

如下图:

Ⅴ.总结:

以上述图片的B+树为例,

B+树里各个结点实际存放在磁盘里,也就是外存中,操作系统对于磁盘的读写一般是以磁盘块为单位,所以一般来说B+树的一个结点就会存放在某一个磁盘块中,所有的结点都是存放在不同的磁盘块中的,因此对于B+树的查找其实是这样的过程:首先要从根结点开始查找,系统在背后做的就是会找到这个根结点它到底是存放在哪一个磁盘块中,比如存放在a磁盘块中,那么接下来会把a磁盘块读取到内存当中,因为只有把数据读取到内存中,计算机才可以处理查询这些数据,接下来就可以确定要查找的关键字应该要到哪个分支上找,假设此时要查找关键字42,那么通过内存当中刚才读入的这一块的信息可以知道,42这个关键字应该是往56所指向的b分块去找,得知b分块存放的位置后,接下来系统就会把这个分块相关的数据再次读入内存,之后计算机就可以查询这个分块内的数据,会发现42这个关键字应该存放在42所指的c分块,所以现在通过内存中的这些信息可以知道再往下一层的叶子结点到底存放在磁盘中的哪一个位置,所以接下来操作系统会把这个叶子结点所对应的这一块给读入内存,读入内存之后就可以找到42这个关键字所对应的记录应该是存放在磁盘中的哪一个位置,所以接下来只需要再从磁盘中读出这个"记录"相关的数据信息即可,

所以对于B+树的查找,每查找一层的结点,其实都需要进行一次读磁盘的操作,直到找到最下面的叶子结点。

其实对于B树的查找也是同理,B树的结点也是存放在一个一个的磁盘块中,所以查找B树的过程中,每查找一层的结点,也需要进行一次读取磁盘的操作,

如下图:

如上图,

其实计算机读磁盘这个操作的时间开销是很大的,因为磁盘是一种慢速设备,所以每一次读取一个磁盘块,都需要花费很长的时间,因此也就意味着B+树的高度越高,在整个查找的过程中所需要读磁盘的次数也会越多,这就会导致查找速度很慢,那么如何使得B+树的高度变小呢?

B+树中每一个结点包含的关键字数量越多,就意味着这棵树总的高度可能会越低,由于B+树中一个一个的结点存放在一个一个的磁盘块中,意味着关键字也存放在磁盘块中,而每一个磁盘块其实大小是固定的,比如只有1KB的大小,既然磁盘块的大小固定,那么就要让一个磁盘块中包含尽可能多的关键字的信息,这就是为什么B+树要设计原因成每一个非叶子结点即分支结点并不包含关键字对应记录的存储地址,这就会导致一份关键字的数据可以花更少的空间来存储,使得一块磁盘可以存更多的关键字,

而B树中,在一个结点里除了要保存关键字的信息外,还需要保存和这个关键字相对应的"记录"的信息,那这个"记录"的地址也需要耗费一定的存储空间,所以B树的这种设计方法会导致在有限的磁盘块中只能保存更少的关键字,而B+树中可以让一个磁盘块保存更多的关键字,也就是可以让B+树的阶更大即分支树尽可能地多,它的分支树越多,就会使得B+树越矮,而B+树越矮,就会导致在查询某个"记录"的时候读磁盘的次数越少,从而使得查找的速度更快,

这就是B+树和B树最本质的区别,

如下图:

如上图,

现在很多关系型数据库如MySQL的"索引"功能就是用B+树实现的。


六.总结:

  • 结点太"空"会导致分叉树变少,使得树的高度变高,查找效率降低



网站公告

今日签到

点亮在社区的每一天
去签到