B树的原理与实现
一、前置知识
1. 三级存储结构
计算机存储结构简单的描述有cpu上的寄存器、内存、磁盘。读取速度从磁盘到寄存器依次增快,存储容量从磁盘到寄存器依次减少。
2. 磁盘的物理结构与读取方式
磁盘按扇区存储,每次存取则去寻找对应扇区,而每一次寻址的速度都是较慢的。
3. Btree
Btree的出现是为了优化磁盘的读取次数。B树的存储结构可以有效的减少磁盘io次数。
B树,是指多叉搜索树,即一个节点上存储多个关键字。一颗M阶B树的定义是指,这颗树是M叉树,一个节点上最多存储M-1个关键字。
一颗M阶B树有如下规范:
- 每个节点至多拥有M棵子树
- 根节点至少拥有两颗子树
- 除根节点之外的所有节点至少拥有ceil(M/2)棵子树
- 所有叶子节点都在同一层
- 有k棵子树的节点分支存在k-1个关键字,并且关键字按照递增顺序排序
- 关键字数量满足ceil(M/2)-1 <= n <= M-1
很明显,B树的一颗平衡树,而B树的平衡依靠的是插入数据时的分裂和删除数据时的借位与合并。
二、Btree的实现
宏观上,B树节点的插入和删除都是在叶子节点上发生的。
(现定义Btree的阶数为M,节点的关键字为K。且由于奇数阶Btree的实现逻辑相对复杂,因此我们仅讨论并实现偶数阶b树)
1. Btree节点的分裂
B树节点的分裂是发生在关键字数量达到最大值的节点上的。分裂操作是,将节点中间的关键字提升插入父节点,再将两边的关键字集合分别作为两个子树存储。因此,分裂代码的实现的传入参数应带有分裂节点的父节点。如果分裂节点是根节点的话,则应生成一个新的空节点作为根节点的父节点。
2. Btree的插入
第一步,找到合适的节点
如果当前节点不是叶子节点,
则插入是发生在此节点的子树上,找到合适的子树。
当子树的节点k == M-1时,子树节点发生分裂,重新从父节点找到合适的插入位置
如果子树的节点k < M-1,则在子树上查找合适的节点
如果当前节点是叶子节点,
则key插入在此节点
第二步,插入key
节点中的keys是按照递增排序的,因此将key插入叶子节点,要找到合适的插入位置。
注:因为在进行分裂时,我们需要传入父节点,所以我们一般是通过父节点去判断子节点是否需要分裂。在这种代码实现下,根节点是不会经过这个逻辑判断的,因此,在每一次宏观插入时,都要先单独分析一下根节点是否需要分裂。
3. Btree的删除
第一步,找到要删除的key所在的节点
如果删除key不在当前节点,且当前节点不是叶子节点,则删除发生在该节点的某一子树上
判断子树K==ceil(M/2)-1,
则向相邻兄弟子树借位,相邻左树(右树)K >ceil(M/2)-1,将父节点连接子树和相邻子树的key移到子树,用相邻左树(右树)的最后(最前)一个key覆盖父节点的key,次数相邻树的key变少,则相邻树的子节点也要相应移动
相邻树无法借位,即相邻兄弟子树K==ceil(M/2)-1,则进行合并。将相邻子树+当前子树+父节点的key下沉合并为一个节点作为父节点的子树。
判断子树K>ceil(M/2)-1,递归找到要删除的key所在的节点
删除key在当前节点,在此节点执行删除操作
第二步,删除操作
当前节点是叶子节点,直接删除
当前节点不是叶子节点,
判断当前key的左(右)子树K>ceil(M/2)-1,将key与左(右)子树的任意key替换,再在子树上递归执行删除操作
当前key的左右子树K == ceil(M/2)-1,执行合并,再在合并后的节点上递归执行删除操作
注:在执行合并操作时,有时候会合并到只有一个key的根节点,此时要注意根节点的变化
——————————————————————————————————————————————————————————————————————————
github:https://github.com/illusorycat/Btree.git
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
以下为随手笔记,
多线程下B树的线程安全?
锁根节点? 可以,但是这样子锁的强度太大了
锁子树 可以,因为b树的没有指向父节点的指针,
磁盘的慢是慢在寻址
为了在节点数相同的情况下,减少层高,降低寻址次数(多叉树)
btree
每个节点4k
一般M选择偶数,则最大key数为基数,在需要分裂的时候,可以直接选择最中间的,方便分裂
b树在添加key的时候,分裂有两种情况:
1. 只有个根节点的时候,1分裂成三个节点
2. 其余情况,1分裂两个节点,把一个key提到父节点
插入的时候,两个步骤
1. 找到对应的节点
2. 对节点的kry对比,找到合适的位置
插入的数据是插在叶子节点的
删除的时候
第一步:找到要删除的key
1. 递归到的子树,key数量等于最小key数
A借位(向同级节点)
a 从前面的子树借,(前面子树的key大于ceil(m/2)-1)
b 从后面的子树借,(不满足上一条,且后面子树的key大于ceil(m/2)-1)
B 合并(不满足借位)
2. 递归到的子树,key数量大于最小key数,直接进行
第二步:删除是发生在叶子节点的
1. 是叶子节点,直接删除
2. 不是叶子节点,
A 借位
a 向左子树借位,(左子树的key大于ceil(m/2)-1)
b 向右子树借位,(右子树的key大于ceil(m/2)-1)
B 合并
将该节点和左右子树合并,
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
扔个b+树的图
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现