本文将为大家介绍B树和B+树,首先介绍了B树的应用场景,为什么需要B树;然后介绍了B树的查询和插入过程;最后谈了B+树针对B树的改进。
在谈B树之前,先说一下B树所针对的应用场景。那么B树是用来做什么的呢? B树是一种为辅助存储设计的一种数据结构,普遍运用在数据库和文件系统中。举个例子来说,数据库大家肯定都不陌生,比如现在有一张表,其中有100万条记录,现在要查找查找其中的某条数据,如何快速地从100万条记录中找到需要的那条记录呢?大家的第一反应肯定是二叉查找树,下面先谈谈为什么二叉树不行。

为什么二叉查找树不行

还是刚刚那个例子,现在一张表中有100万条记录,我们以表的主键来构建一个二叉查找树。先不考虑二叉树不平衡退化成链表的情况,假设是理想情况,这个二叉树是完全平衡的。那么构建完成之后应该是下面这个样子的(示意图)

树的高度应该为

现在开始查找,加入我们要查找值为100的节点,怎么找呢?首先应该获取树的根节点。一般来说,表的索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。也就是说我们构建的二叉查找树太大了,内存放不下,一般要存在磁盘上,用的时候再从磁盘读入到内存。现在假设我们知道了根节点所在的磁盘位置,那么应该首先将根节点读入内存中,这里进行了一次IO操作,然后判断要找的值比根节点大还是小,100比4大,所以去右子树查找。那么如何找到6所在的节点呢?根节点中存储着6所在节点在磁盘上存放的位置。同样,需要将其先读入内存中,然后再判断继续向下查找,这里又是一次IO操作。下面的过程类似,不再展开。总结一下,查找到我们所需的那条记录大概需要进行20次IO操作,也就是树的高度,因为每向下查找一层,就要进行一次IO操作。
为什么要强调IO操作,而不是在内存中比较的次数呢? 因为磁盘的速度相比内存而言是非常的慢。比如常见的7200RPM的硬盘,摇臂转一圈需要60/7200≈8.33ms,换句话说,让磁盘完整的旋转一圈找到所需要的数据需要8.33ms,这比内存常见的100ns慢100000倍左右,这还不包括移动摇臂的时间。所以在这里制约查找速度的不是比较次数,而是IO操作的次数。换句话说,如果能够减少一次IO操作,那么多在内存中比较100次也无所谓,因为二者的速度相差100000倍。而我们可以认为IO操作的次数就大约等于树的高度,树的高度是如何计算的呢?看一下这个公式

我们想让这个值尽可能的小,就只能让真数小,或者底数大。真数是数据记录的条数,不是我们能决定的。那么底数呢?这个2来自哪呢?当然是来自二叉树中的那个二。那么这个底数能不能变大呢?当然能!!!那就是不要用二叉树,而要用多叉树,这就是我们要说的B树了。

B树是什么

B树也称B-树,它是一颗多路平衡查找树。B树和后面讲到的B+树都是从最简单的二叉树变换而来的。描述一颗B树时需要指定它的阶数,阶数表示了一个节点最多有多少个孩子节点,一般用字母m表示阶数。下面我们来看看B树的定义
(1)树中每个节点至多有m 棵子树(m指的是树的阶);
解释:有的定义说的是每个节点最多有m-1个关键字,是一样的,对于每个节点来说子树的数目等于关键字数目加1,下面会举例说明。
(2)若根结点不是叶子结点,则至少有两棵子树;
解释:当根节点为叶子节点时,可以没有子树;不为叶子节点时,至少有一个关键字,也就是至少有两棵子树。
(3)除根结点之外的所有非叶子结点至少有⌈m/2⌉个子节点;
解释:⌈m/2⌉表示向上取整,比如m=5时,⌈m/2⌉=3,表示至少有3个子节点,当然最多有5个。
(4)所有的非叶子结点中包含以下数据:(n,A0,K1,A1,K2,…,Kn,An)
解释:n为节点中关键字的数目,Ki(i=1,2,…,n)为关键字,且Ki<Ki+1
Ai 为指向孩子节点的指针(i=0,1,…,n),且指针Ai-1 所指子树中所有结点的关键字均小于Ki (i=1,2,…,n),An 所指子树中所有结点的关键字均大于Kn。(这里也可以看到对于每个节点来说子节点数量比关键字数量多1)
(5)所有的叶子结点都出现在同一层次上,即所有叶节点具有相同的深度,等于树高度。叶子节点除了包含了关键字和关键字记录的指针外也有指向其子节点的指针只不过其指针地址都为null。
下面具体举个例子说明,相信看了这个例子会对上面的定义有更加深刻的理解。

B树的查找

以上图为例,假设要查找15的节点,查找流程如下
(1)获取根节点的关键字进行比较,当前根节点关键字为50,50>15,所以找到指向左边的子节点;
(2)拿到关键字10和30,10<15<30 所以直接找到10和30中间的指针指向的子节点;
(3)拿到关键字15,就是要查找的目标值, 所以直接返回关键字和指针信息(如果树结构里面没有包含所要查找的节点则返回null)
至此我们便完成了B树的查找过程,比较简单,且与二叉查找树类似。
关于B树的插入操作,可以参考【为什么有红黑树?什么是红黑树?看完这篇你就明白了】这篇推文中关于2-3树的插入操作的详细介绍,其实2-3树就是一种特殊的B树。限于篇幅,本文不再赘述。

从B树到B+树

B+树是从B树衍生而来的,比B树更具有优越性。B+树相对于B树主要做了两点改进:
(1)非叶结点仅具有索引作用,跟记录有关的信息均存放在叶结点中。
解释:B+树的非叶子节点不保存关键字记录的指针,只进行数据索引;B+树叶子节点保存了父节点的所有关键字记录的指针,所有数据地址必须要到叶子节点才能获取到。还是举刚才B树的例子,B树中根节点的关键字为50。假如我们就要查找主键为50的记录,那么在B树中只需进行一次IO操作,将根节点读入内存,便可以直接命中。而在B+树中则不同,B+树中任何查询必须要到叶子节点才能获取到,所以每次数据查询的次数都一样,这一点和B树有很大区别。
(2)树的所有叶节点构成一个有序链表,可以按照关键字排序的次序遍历全部记录。
解释:这样做有两点好处。一是进行范围查询更快,数据紧密性很高,缓存的命中率也会比B树高。二是B+树全节点遍历更快,B+树遍历整棵树只需要遍历所有的叶子节点即可,而不需要像B树一样需要对每一层进行遍历,这有利于数据库做全表扫描。
推荐阅读
为什么有红黑树?什么是红黑树?看完这篇你就明白了
都2020年了,听说你还不会归并排序?手把手教你手写归并排序算法
为什么会有多线程?什么是线程安全?如何保证线程安全?

觉得文章有用的话,点赞+关注呗,好让更多的人看到这篇文章,也激励博主写出更多的好文章。
更多关于算法、数据结构和计算机基础知识的内容,欢迎扫码关注我的原创公众号「超悦编程」。

《深入浅出话数据结构》系列之什么是B树、B+树?为什么二叉查找树不行?的更多相关文章

  1. 【分享】深入浅出WPF全系列教程及源码

    本人10月份提出离职,可是交接非常慢,预计年底才会交接完,趁着交接之际,自学了一下WPF,由于这是微软未来的发展趋势,自WIN7以来包含前不久公布的WIN8,核心还是WPF,在此,将自己的学习成果做一 ...

  2. 【【分享】深入浅出WPF全系列教程及源码 】

    因为原书作者的一再要求,在此声明,本书中的部分内容引用了原书名为<深入浅出WPF>的部分内容,假设博文不能满足你现有的学习须要,能够购买正版图书! 本人10月份提出离职,可是交接非常慢,预 ...

  3. 【分享】深入浅出WPF全系列教程及源代码

    来源:http://blog.csdn.net/yishuaijun/article/details/21345893 本来想一篇一篇复制的.但是考虑到别人已经做过了,就没有必要了吧,就给大家一个目录 ...

  4. JAVA数据结构系列 栈

    java数据结构系列之栈 手写栈 1.利用链表做出栈,因为栈的特殊,插入删除操作都是在栈顶进行,链表不用担心栈的长度,所以链表再合适不过了,非常好用,不过它在插入和删除元素的时候,速度比数组栈慢,因为 ...

  5. [js高手之路]深入浅出webpack教程系列6-插件使用之html-webpack-plugin配置(下)

    上文我们对html-webpack-plugin的实例htmlWebpackPlugin进行了遍历分析,讲解了几个常用属性( inject, minify )以及自定义属性的添加,本文,我们继续深入他 ...

  6. [js高手之路]深入浅出webpack教程系列5-插件使用之html-webpack-plugin配置(中)

    上文我们讲到了options的配置和获取数据的方式,本文,我们继续深入options的配置 一.html-webpack-plugin插件中的options除了自己定义了一些基本配置外,我们是可以任意 ...

  7. [js高手之路]深入浅出webpack教程系列4-插件使用之html-webpack-plugin配置(上)

    还记得我们上文中的index.html文件吗? 那里面的script标签还是写死的index.bundle.js文件,那么怎么把他们变成动态的index.html文件,这个动态生成的index.htm ...

  8. [js高手之路]深入浅出webpack教程系列3-配置文件webpack.config.js详解(下)

    本文继续接着上文,继续写下webpack.config.js的其他配置用法. 一.把两个文件打包成一个,entry怎么配置? 在上文中的webpack.dev.config.js中,用数组配置entr ...

  9. [js高手之路]深入浅出webpack教程系列7-( babel-loader,css-loader,style-loader)的用法

    什么是loader呢,官方解释为文件的预处理器,通俗点说webpack在处理静态资源的时候,需要加载各种loader,比如,html文件,要用html-loader, css文件要用css-loade ...

随机推荐

  1. pytorch BiLSTM+CRF代码详解 重点

    一. BILSTM + CRF介绍 https://www.jianshu.com/p/97cb3b6db573 1.介绍 基于神经网络的方法,在命名实体识别任务中非常流行和普遍. 如果你不知道Bi- ...

  2. webpack学习(四)配置plugins

    1 plugins是什么??? 如果学过vue和react肯定知道生命周期函数,而生命周期函数实际上就是当程序运行在某个时刻一定会发生的函数. plugins其实也是如此,我们在项目中配置相应的plu ...

  3. 140种Python标准库、第三方库和外部工具

    导读:Python数据工具箱涵盖从数据源到数据可视化的完整流程中涉及到的常用库.函数和外部工具.其中既有Python内置函数和标准库,又有第三方库和工具. 这些库可用于文件读写.网络抓取和解析.数据连 ...

  4. Notice: Use of undefined constant - assumed ' '

    昨天看手册的时候有两个范例,懒得写了,直接复制,测试一下,结果报Notice; 反复检查无果,最后, 手动敲了一遍,居然正常了,汗.... 总结:偷懒害人

  5. P1073 奇数还是偶数

    题目描述 给你 \(N(1 \le N \le 1000)\) 个数,你需要判断每个数是奇数还是偶数. 输入格式 输入的第一行包含一个整数 \(N(1 \le N \le 1000)\) . 接下来 ...

  6. Server,Servlet,ServletConfig,ServletContext,Session,Request,Response

    Server流程 解析URL->找到应用->找到Servlet->实例化Servlet->调用init->调用service->返回响应->调用destroy ...

  7. linux测试 scullpipe 驱动

    我们已经见到了 scullpipe 驱动如何实现阻塞 I/O. 如果你想试一试, 这个驱动的源码 可在剩下的本书例子中找到. 阻塞 I/O 的动作可通过打开 2 个窗口见到. 第一个可运行 一个命令诸 ...

  8. 备战省赛组队训练赛第十四场(UPC)

    codeforces:传送门 upc:传送门 外来题解: [1]:https://blog.csdn.net/ccsu_cat/article/details/86707446 [2]:https:/ ...

  9. 2018-8-10-win10-uwp-修改Pivot-Header-颜色

    title author date CreateTime categories win10 uwp 修改Pivot Header 颜色 lindexi 2018-08-10 19:17:19 +080 ...

  10. POJ - 3415 Common Substrings (后缀数组)

    A substring of a string T is defined as: T( i, k)= TiTi +1... Ti+k -1, 1≤ i≤ i+k-1≤| T|. Given two s ...