跟着B站王道考研视频写的笔记,图片来自王道考研
B+树
1. 分块查找
区分下顺序表和数组的概念,因为刷题没遇到过什么是顺序表:
顺序表指的是按顺序存储的方式存储的线性表,即里面的元素是一个接一个连续存储在内存空间里的线性表,而不是说里面的元素是有序的(注意有序和顺序的含义是不一样的),它区别于单链表这种,链表的话就不需要表中元素在连续内存里存储了(它是非顺序存储方式),而是可以依靠指针指向下一个元素存储的位置,所以单链表就不需要连续的内存空间去存储。
当然数组相比顺序表,其含义的范围更大,因为数组是物理内存上的角度,而顺序表是逻辑结构,显然数组不仅可以放顺序表这种数据结构,还可以放二叉树这种非线性的数据结构。
分块查找介绍:
对于一个无序的顺序表,对其分成几个小区间(分块),使其块内的最大元素小于下一块的最小元素(从这就感觉这个分块查找不太具有普适性),对每块的最大值提出去建立一个索引表,索引表内除了保存每块的最大关键字,还保存了每个分块的存储区间。分块查找的一个特点是块内无序,块间有序
当我们想要查找一个数时,就先在其索引表上二分查找,如果索引表不包含目标关键字,则二分查找索引表最终停在low > high,要在low所指分块中查找,然后根据low所在分块存储的存储区间,去顺序表的分块中查找(块内无序,只能顺序查找)
2. B+树的结构
B+树层与层之间的关系很像分块查找,即这一层节点的最大值存储在上层结点中,不过相比于分块查找,B+树的块内是有序的,即结点内关键字是有序的
B+树和B树很重要的不同是:结点的子树个数与该结点关键字个数相等!
(当然还有其它区别,比如B树的关键字不会重复出现,而B+树会,主要对比两者的性质或者定义)
回顾下B树:
它的结点的子树个数为当前结点关键字数+1
因为B树是平衡二叉查找树,它的左子树要比当前值小,右子树的值比当前关键字值大,比如5和11之间是6,8,9,而5的左边是1和3
而且注意到B+树叶子结点之间有指针!
一棵m阶的B+树需满足下列条件:
1)每个分支结点最多有m棵子树(m个孩子节点)。
0)除了根结点,每个结点至少有两棵子树,其它每个分支结点至少有[m / 2]棵子树。
3)结点的子树个数与关键字个数相等
4)所有叶结点包含全部关键字及指向相应记录的指针,叶结点中将关键字按大小顺序排列,并且相邻叶结点按大小顺序相互链接起来。
5)所有分支结点中仅包括它的各个子结点中关键字的最大值及指向其子结点的指针(比如根节点15就是只存了左子节点的最大值,56同样)
解释下第四点:
比如B+树上所有关键字是代表学生学号,某学生学号48,那么我们按照B+树找到了48,就可以根据48中“指向相应记录的指针”去找到那个记录,就是学生的相关信息。B树叶子节点是指向空,而B+树是指向存储了相关信息的内存。
而且,可以看到,其实所有的关键字都会在叶子节点里(这点也和B树不一样,和很多二叉树也都不一样),而且叶子节点之间还是有序的!所以也可以把叶子节点连起来(如图),然后B+树也保存了一个指针P指向最左边的个叶子结点,使得可以从左往右直接在叶子节点上顺序查找。(没法二分,有序是结点与结点之间,又不是关键字连成有序链表了)
3. B+树的查找
1)从根结点开始找:
比如要在上面的B+树中查找元素9,那么9小于15,所以在其左子树上找,找到9没完,因为这样得不到关键字9对应的信息,所以还得往下找!
往下到叶子节点,从左往右依次检查,找到9,然后读出相应的记录。
查找失败的案例,比如7,比如2,会发现,都要到达叶子结点才能确认是否查找失败。因为当前结点存的是其叶子结点关键字中最大的值,不知道它下面还有没有目标值。叶子结点上面结点的信息并不能真实反映某个关键字是否真实存在。
所以有这个总结: 在B+树中,无论查找成功与否,最终一定都要走到最下面一层结点
相比之下,B树可以在树的中间层确认找到与否。
2)通过指针P顺序查找:
在叶子结点上从左往右顺序查找,比如9,一直1,3,6,8到9,然后取出记录
补充:
实际上,B树的每个结点都包含了关键字对应的记录的存储地址,而B+树只有叶子节点才保存有指向记录的指针,前面容易误以为只有B+树会保存额外的记录
4. B+树比B树好在哪
那B+树每次都要找到叶子节点才能确认待查找的关键字是不是在树中,并且到叶子结点才能找到对应关键字的记录,而B树在其他结点就可以,岂不是B树查找效率高?
想来想去,估计是B树那种存储方法占用的内存太大了,每个结点的关键字都存一下对应的记录。
王道中有以下总结:
在B+树中,非叶结点不含有该关键字对应记录的存储地址。可以使一个磁盘块包含更多个关键字,使得B+树的阶更大,树的高度更矮,这样CPU读磁盘次数更少,查找更快。
因为B树、B+树其实存放在磁盘里,操作系统对磁盘的读写是以磁盘块为单位,所以B树、B+的一个结点就存储在一个磁盘块中(结点——磁盘块),所有的结点就存放在不同磁盘块中。
所以B+树的查找过程其实是这样的:
如果我们从根结点开始查找,那么操作系统先找到存放了根结点的磁盘块,并将其读入内存,经过操作系统的处理,找到了待查找关键字在哪个分支里,比如要找42,那么得往56所指分块找,所以需要将该分块读入内存,然后找到42,接着同理找到叶子结点,接着把叶子结点读入内存,然后根据42的指针,就知道相关记录存放在磁盘的哪个位置了,这样操作系统就能从磁盘里读出该记录,从而完成信息查找。
每查找一个结点(一层找一个)就要进行一次读磁盘的操作,B树也是一样,而计算机读磁盘的操作,其时间开销很大(磁盘是一种慢速设备,相对于CPU读内存,每读取一次磁盘块都需要花费很多时间),所以显然,树越高,那需要查找的次数增多,需要将磁盘块读入内存的次数也越多,从而使得时间变慢。
而每个结点包含关键字的数量越多,树的总高度也越低;每个结点存放在一个磁盘块中,而磁盘块的大小是一定的,比如1KB,在B树中,除了保存关键字信息还要保存指向关键字相对应的记录信息的指针(也就是多存储了些指针,因为记录本身比较大,不会存在磁盘块里),这样的话能存放的关键字个数也变少了!
所以像关系型数据库的"索引"功能,就是B+树实现的(如MySQL)