一文带你入门B树和B+树

B树和B+树,其实在各大论坛平台上总会看到这两个东西。但是他们说的又有种“高手云集”的感觉 —— 他们都会,就我不会,连插嘴的功夫都没有。这篇文章就来简单的讲解一下B树和B+树,下次再遇见这种话题,也不用再尴尬窘迫了。


在解读这两个树之前呢,先解释一下 B-树 这个东西。

其实很多人不敢去了解 B树 的有关内容,为什么呢,一打听这个东西,就有什么 B-树、B树、B+树 ,3个东西!太多了!实际上不是这样。

现在打开百度百科搜索 B树 ,你就会发现,百度百科会直接跳转到 B-树 里。什么意思呢,没错,没有 B-树 ,所谓 B-树 就是 B树

B树 的英文名:B-Tree,国内很多人喜欢翻译的时候不去掉中间的符号,把他叫做 B-树,实际上它就是 B树 。以后千万不要说“还有个B减树呢”。


一、动态查找树

动态查找树主要有:二叉查找树、平衡二叉查找树、红黑树、B树/B+树/B*树。

前三个树是非常典型的二叉查找树的结构,查找数据的时间复杂度为 O(logN) ,与树的深度有关。降低树的深度就能提高查找的效率。

但是大规模的数据存储,实现索引查询的这么一个实际背景下,树节点存储的元素是有限的。而元素的数量非常多的话,查找就变成了节点内部的线性查找了,这样一来树的深度很大,硬盘的IO读写就非常频繁,查询效率就十分低下。如何能降低树的深度但又保证元素的数量呢,一个基本的想法就是使用多叉树结构,从这个想法我们就提出了新的查找树结构——多路查找树。再根据平衡二叉树的启发,自然会想到平衡多路查找树,也就是我们要说的 B树,这里的字母 B 指的就是 Banlanced 平衡。

二、B树

1. 什么是B树

B树是为了磁盘或其它存储设备而设计的一种多叉平衡查找树。与红黑树很相似,但在降低磁盘IO操作方面要更好一些。许多数据库系统都一般使用B树或者B树的各种变形结构

B树与红黑树最大的不同点是:B树的结点可以有许多子女。但是在B树里,一棵含n个结点的B树的高度也为 O(logN) ,与红黑树一样,但可能比红黑树的高度要更小,因为它的分支因子比较大。所以,B树可以在 O(logN) 的时间内,实现各种插入删除等动态集合操作。

2. B树的定义

来自《算法导论》相关内容

  1. 所有叶子节点到根节点的路径长度相同,即具有相同的高度;
  2. 每个非叶子和非根节点(即内部节点)至少有t-1个孩子节点;根至少2个孩子
  3. 每个节点最多有2t个孩子节点。
  4. 每个节点内的键都是递增的
  5. 每个节点的孩子比key的个数多1

3. B树的特点(概念性东西)

  1. 树中每个结点最多含有m个孩子(m>=2);

  2. 除根结点和叶子结点外,其它每个结点至少有 [ceil(m / 2)] 个孩子,ceil(x)的作用是向上取整

  3. 若根结点不是叶子结点,则至少有2个孩子,特殊情况是整个树只有一个根节点

  4. 所有叶子结点都出现在同一层,叶子结点没有孩子也没有指向孩子的指针。

  5. 每个非终端结点中包含有n个关键字信息: (n,P0,K1,P1,K2,P2,…,Kn,Pn)。其中:

    a. Ki (i=1…n)为关键字,且关键字按顺序升序排序 K(i-1)< Ki 。
    b. Pi为指向子树根的接点,且指针P(i-1)指向子树种所有结点的关键字均小于Ki,但都大于 K(i-1) 。
    c. 关键字的个数n必须满足: [ceil(m / 2)-1]<= n <= m-1。

4. 深入理解B树

本文只提供简单的深入理解,使用图文方式介绍B树的运行流程

若想进一步理解B树,可以移步查看其他大佬的文章基于Java的B树实现

先简单的用代码构建一下B树的基本样子

// 存储数据用的节点
private static class Entry<K, V> {
    private K key;
    private V value;

    public Entry(K k, V v) {
        this.key = k;
        this.value = v;
    }
}

// B树节点
private static class BTreeNode<K, V> {
    // 数据节点
    private List<Entry<K, V>> entries;
    // 子节点
    private List<BTreeNode<K, V>> children;
    // 是否为叶子节点
    private boolean leaf;

    private BTreeNode() {
        entries = new ArrayList<>();
        children = new ArrayList<>();
        leaf = false;
    }
}

我们不难发现,B树是使用 Entry 存储实际数据,用 BTreeNode 存储指向区域。

4.1 查询

如图所示,假设我们要搜索 S 的值。

图片

  1. 首先判断根节点 M ,如果 M 就是我们需要的结果就直接返回。这里显然不是,去对应的下标指向递归搜索。( S 排在 M 之后,所以访问 QT)。
  2. 拿到两个节点值 Q 和 T ,Q < S < T,所以访问中间的指向区域,找到 RS 。
  3. 拿到两个节点值 R 和 S,S 是我们需要搜索的值,根据 Entry 直接获取 value 返回即可。
  4. 如果访问不到的话就直接返回 null 。

4.2 插入

用绘图的方式展示插入的过程。给定一组关键字{20,30,50,52,60,68,70},给出创建一颗3阶B树的过程

  1. 由 3阶B树 可知, m = 3。所以除了根节点以外,非叶子结点至少有 ceil(3/2)-1=1 个关键字,最多有 3-1=2 个关键字。所以我们可以插入 20,30 两个节点。

在这里插入图片描述

  1. 下一步,插入50,此时节点拥有3个关键字,不符合条件,所以需要一次分裂。分裂的方式是按序标号后取中间关键字 ceil(3/2)=2 ,把30当作中间关键字分裂出来,其他两个节点当作30这个新节点的左右孩子。

在这里插入图片描述

  1. 插入 52 ,没有超过2个关键字,正常插入。

在这里插入图片描述

  1. 插入60,此时又达到了3个关键字,执行分裂操作。

在这里插入图片描述

  1. 发现23可以和52合并,再执行合并操作

在这里插入图片描述

  1. 插入68

在这里插入图片描述

  1. 插入70 ,进行分裂、合并、再分裂的操作

在这里插入图片描述
在这里插入图片描述

  1. 插入完成

在这里插入图片描述

4.3 删除

删除节点的难点在于要保证删除之后的节点的关键字满足 关键字个数>= ceil(m/2)-1,可以简单的分成两种情况,删除的是终端节点,和不是终端节点

4.3.1 删除终端节点

删除终端节点也分成3种情况。

  1. 结点内关键字数量大于ceil(m/2)-1,这时删除这个关键字不会破坏B树的定义要求。所以直接删除。不再赘述。

在这里插入图片描述

比如上图,我们想删除9,那就可以直接删除,并不会影响B树的结构。

  1. 结点内关键字数量等于ceil(m/2)-1,并且其左右兄弟结点中存在关键字数量大于ceil(m/2)-1的结点,则去兄弟结点中借关键字。

在这里插入图片描述

还是这个图,我们想删除2,那就要从右兄弟中拿来一个节点,然后按照大小调整,也就是变成下面这样。
在这里插入图片描述

  1. 结点内关键字数量等于ceil(m/2)-1,并且其左右兄弟结点中不存在关键字数量大于ceil(m/2)-1的结点,则需要进行结点合并。

在这里插入图片描述

这次我们想要删除 22 ,22节点的左兄弟只有1个关键字,需要节点合并。从上一层的结点取关键字与下一层结点合并,如果左右兄弟结点都存在则方式不唯一,这里22只有左兄弟,所以只能把16和20和并成一个结点。

在这里插入图片描述

4.3.2 删除非终端节点

删除非终端节点的方式是先把这个节点变成终端节点,然后再进行删除,这里也能分成两种情况。

  1. 存在关键字数量大于ceil(m/2)-1结点的左子树或者右子树。

    还是之前的图

在这里插入图片描述

这次我们删除节点 10,首先要找到和他相邻的两个节点 —— 9 和 11。发现两者皆满足其所在结点上关键字数量大于ceil(3/2)=1,所以与任意一个替换均可,这里我们替换 9。为了方便不再展示全部的树形图。

在这里插入图片描述

  1. 左右子树关键字数量均等于ceil(m/2)-1

在这里插入图片描述

这里我们删除节点 20 ,左右子树关键字数量均等于ceil(3/2)-1=1,则将这两个左右子树结点合并,然后删除待删除关键字。

在这里插入图片描述

这其实适合我们刚才删除终端结点中是一样的:要删除结点20,我们可以先将20和22进行交换,接着我们的目的就变成删除一个终端结点20,删除这样一个终端结点,但是的关键字数目已经等于最小关键字数目要求ceil(3/2)-1=1了,这就是删除终端结点的第三种情况。我们需要进行合并,从上层结点中借一个22来与16进行合并,合并之后可将结点20删除,让14右边指针指向合并后的新结点,其最终效果是一样的。

三、B+树

B+树是常用于数据库和操作系统的文件系统中的一种用于查找的数据结构。

  1. 在B+树中,具有n个关键字的结点只含有n棵子树,即每个关键字对应一棵子树;而在B树中,具有n个结点的关键字含有(n+1)棵子树。
  2. 在B+树中,每个结点关键字个数n的范围是ceil(m/2)<=n<=m(根节点1<=n<=m),在B树中,每个结点(非根内部结点)关键字个数n的范围是ceil(m/2)-1<=n<=m-1(根节点:1<=n<=m-1)。
  3. 在B+树中,叶结点包含信息,所有非叶结点仅起到索引作用,非叶结点中的每个索引项只含有对应子树最大关键字和指向该子树的指针,不含有该关键字对应记录的存储地址。
  4. 在B+树中,叶结点包含了全部关键字,即在非叶结点中出现的关键字也会出现在叶节点中;而在B树中,叶结点包含的关键字和其他节点包含的关键字是不重复的。
  5. 在B+树中,有一个指针指向关键字最小的叶子结点,所有叶子结点连接成一个链表

在这里插入图片描述

该子树的指针,不含有该关键字对应记录的存储地址。
4. 在B+树中,叶结点包含了全部关键字,即在非叶结点中出现的关键字也会出现在叶节点中;而在B树中,叶结点包含的关键字和其他节点包含的关键字是不重复的。
5. 在B+树中,有一个指针指向关键字最小的叶子结点,所有叶子结点连接成一个链表

本文借鉴:https://blog.csdn.net/weixin_45750207/article/details/119678602

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值