【数据结构】B+树

B+树的定义

B树是B+树针对数据库的变种,拥有比B树更高的查询效率。

下面,我们看着图,先搞清楚它的定义:

在这里插入图片描述

一个结点的最大孩子个数称为树的阶,通常用m表示。一棵m阶B+树要么是空树,要么满足以下定义:

(1)一个结点的结构是:(K1,P1,K2,…,Kn,Pn),其中P为指向子树的指针,K为关键字,它们一一对应

(2)对于根结点,如果它本身不是叶子结点,至少拥有 1 个关键字,即至少拥有 1 棵子树

(3)对于除根结点之外的所有结点,至少拥有⌈m/2⌉ 棵子树,即至少拥有 ⌈m/2⌉ 个关键字

(4)为了满足阶的定义,任何结点最多拥有 m 个子树,即最多拥有 m 个关键字

(5)B+树的所有数据保存在叶子结点,且叶子结点由指针链接起来

 
 

B+树的查找

查找某一行记录

还是看图举栗子,比如查找30:

在这里插入图片描述

不难看出,和B树的查询如出一辙,仍旧是 ① 从磁盘读取结点 ② 在内存中顺序/二分查找关键字;

值得注意的是,B+树与B树相比,所有的数据都是放在叶子结点上的,这导致了:

(1)非叶子结点仅仅用于标识范围,所以查找过程不会像B树那样有可能在中途结束,而是每次必须查找到叶子结点。所以说,B+树比B树稳定。

(2)非叶子结点上不存储数据,所以盘块(存一个结点)上可以存储更多的向下索引。因此,B+树比B树更加矮胖,I/O次数会更少,这也是B+树查找性能优于B树的其中一个重要原因。


范围查询

还还还是看图举栗子,比如查找 20~40 :

在这里插入图片描述

首先,还是不断根据索引读磁盘、内存排序找索引,找了20;接下来的操作非常简单,即通过叶子结点间的指针一口气找到40之前的所有数据!!!正是因为数据的有序性和叶子结点间的指针,B+树的范围查询性能极高。

而在B树中,是如何进行范围查找的呢?先找到20,然后从20开始不断中序遍历。中序遍历的过程中,同时也找到了父结点中的目标数据,但是,中序遍历的磁盘I/O开销非常可怕。


聚集索引和非聚集索引

什么是聚集索引?

数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同。由于表的物理存储位置固定,索引一个表中只能拥有一个聚集索引。

什么是非聚集索引?

以主键以外的列值作为键值构建的索引,我们称之为非聚集索引。显然对于加非聚集索引所用的列,逻辑顺序与物理顺序是无关的。

什么是二次查询问题?

如果使用非聚集索引作为查询条件,且查询列中包含了没有任何被任何索引覆盖的列,那么就会进行两次查询——第一次根据非聚集索引查询聚集索引(主键),第二次根据聚集索引查询结果。

举个例子吧:

我们有一张表:users(id,name,age),其中id加了聚集索引,name加了非聚集索引。

select id, name from users where name="Alice"	-- 不会发生二次查询,id和name都已被索引覆盖
select id, age from users where name="Cocoa"	-- 会发生二次查询,age未被索引覆盖

 
 

B+树的插入

和B树的插入基本一致:

如果插入后结点满足定义,则直接插入;如果插入后结点不满足定义,则结点分裂。
值得注意的是,B+树非叶子结点并不存数据,只是索引值。

看图理解非常简单,这是个4阶B+树:

在这里插入图片描述

 
 

B+树的删除

与B树的删除相比,思路基本一致,但是简单很多很多 >_<

跟着下面的图走一遍(4阶B+树),一定可以学会!


首先明确,B+树没有B树那种删除非叶子结点的情况

因为B+的非叶子结点不存储数据。


如果删除后仍符合B+树定义,则直接删除。

在这里插入图片描述


如果删除后不符合B+树定义,且兄弟有的借,则直接向兄弟借。

与B树相比非常方便!因为非叶子结点不存数据、叶子结点指针的存在,不需要向B树那样带着父结点旋转了,仅仅需要修改一下父结点对应关键字的值即可。

在这里插入图片描述


如果删除后不符合B+树定义,且兄弟没的借,则与兄弟合并。

与B树相比还是非常方便!不需要带着父结点一起合并,仅仅需要修改一下父结点对应关键字的值即可。

在这里插入图片描述

 
 
 
 
 
 

面试题:谈一谈二叉搜索树、二叉平衡树、B树、B+树的区别?以及它们对于数据库索引的意义。

(0)谈到这四种树,就不得不说一下数据库的索引查询。数据库的数据庞大,是无法一次性读到内存中进行查询的,因此,数据库的查询其实是一个不断读磁盘到内存、在内存中查找下一个磁盘的过程。

(0)查找过程是一个树形结构,这课树的高度即磁盘I/O的次数(不考虑第一个页表常驻内存的情况,且假定一个树结点占一个磁盘盘块)。也就是说,这棵树越矮胖,磁盘I/O次数就越少,查询的性能就越高;而在内存中的查找开销,比起磁盘的I/O开销基本可以忽略不计。

(1)二叉搜索树不是平衡的,其余三种是平衡的。平衡的意义是什么呢?想象一种极限的情况,二叉平衡树退化为线性链表。磁盘I/O次数极多,查询性能极低。

(2)二叉平衡树不是自平衡的,B树和B+树是自平衡的。平衡二叉树的平衡是通过查找最低不平衡结点并递归调整实现的;自平衡二叉树的平衡是通过局部不平衡的动态优化最终达到全局优化的。

(3)二叉平衡树是二叉的,B树和B+树是多叉的。多叉的意义何在?还是让树变得矮胖,以获得更高的查询效率。

(4)B树的非叶子结点也存储数据,B+树的非叶子结点不存数据。具体的说,B树的关键字有两层含义,一来标识范围,二来指向卫星数据;B+树的关键字只有一层含义,仅仅表示范围。B+树的这个思路有什么好处呢?让一个结点可以存储更多的索引信息,从而让树形更加矮胖;另外,B+树的查询一定会进行到叶子结点,与B树相比更加稳定。

(5)B+树的叶子结点由指针链接起来。这样做的目的很明确,即满足范围查询的需求。

(6)更细节的说,B树结点的结构是(m-1)个关键字和m个索引指针,B+树结点的结构是m个关键字和m个索引指针(索引指针个数即子树个数);B树和B+树结点都最少拥有⌈m/2⌉个索引指针,最多拥有 m 个索引指针,但是关键字个数不同(B树减1,B+树相等)。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

☘️

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
B+树是一种常用的树数据结构,通常用于数据库和操作系统的文件系统中。它具有以下特点: - B+树能够保持数据稳定有序,即所有的数据都按照一定的顺序存储在树中。 - B+树的插入和修改操作具有较稳定的对数时间复杂度,即O(log n)。 - B+树的元素是自底向上插入的,与二叉树相反。 下面是一个C++实现的B+树的代码示例: ```cpp // BPulsTree.h #ifndef BPLUSTREE_H #define BPLUSTREE_H // B+树节点的定义 struct Node { int *keys; // 存储关键字的数组 Node **childPointers; // 存储子节点指针的数组 bool isLeaf; // 是否为叶子节点 int numKeys; // 当前节点的关键字数量 Node *next; // 指向下一个叶子节点的指针 // 构造函数 Node(bool isLeafNode); // 插入关键字 void insert(int key); // 删除关键字 void remove(int key); // 查找关键字 bool search(int key); }; // B+树的定义 class BPlusTree { private: Node *root; // 根节点 public: // 构造函数 BPlusTree(); // 插入关键字 void insert(int key); // 删除关键字 void remove(int key); // 查找关键字 bool search(int key); }; #endif ``` ```cpp // BPulsTree.cpp #include "BPulsTree.h" Node::Node(bool isLeafNode) { keys = new int[3]; // 假设每个节点最多存储3个关键字 childPointers = new Node*[4]; // 假设每个节点最多有4个子节点 isLeaf = isLeafNode; numKeys = 0; next = nullptr; } void Node::insert(int key) { // 插入关键字的逻辑 // ... } void Node::remove(int key) { // 删除关键字的逻辑 // ... } bool Node::search(int key) { // 查找关键字的逻辑 // ... } BPlusTree::BPlusTree() { root = nullptr; } void BPlusTree::insert(int key) { // 插入关键字的逻辑 // ... } void BPlusTree::remove(int key) { // 删除关键字的逻辑 // ... } bool BPlusTree::search(int key) { // 查找关键字的逻辑 // ... } ``` ```cpp // Demo.cpp #include "BPulsTree.h" #include <iostream> int main() { BPlusTree tree; tree.insert(10); tree.insert(20); tree.insert(30); tree.insert(40); tree.insert(50); std::cout << "Search 30: " << (tree.search(30) ? "Found" : "Not found") << std::endl; std::cout << "Search 60: " << (tree.search(60) ? "Found" : "Not found") << std::endl; return 0; } ``` 这是一个简单的B+树的C++实现示例,其中包含了B+树节点的定义和B+树的操作方法。你可以根据需要进行插入、删除和查找操作。在示例中,我们创建了一个B+树对象,并插入了一些关键字,然后进行了查找操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值