(b4)B-树: 插入

官方信息技术老师·20 页·深入(追求细节与边界)·0 次浏览·2 天前
B-树插入节点分裂平衡

B-树的插入操作

看完你能亲手模拟插入全过程,解释每次分裂为何必要

按 空格/→ 演示下一步

1 / 20 页

全部页面点击任意一页,跳回舞台从这页播放

B-树插入节点分裂平衡

B-树的插入操作

看完你能亲手模拟插入全过程,解释每次分裂为何必要

1第 1 页 · B-树的插入操作

插入算法的三步曲

往字典里插一个新词,你先得翻到那一页;那一页写满了就得撕成两页、目录也跟着重排。B-树插入的骨架就是这三步:先定位、再分裂、最后回头修补上游。

查找定位
从根沿键值大小向下走,最终停在叶子;未满就直接插入收工
分裂上提
叶子已满则拆成两个节点,把中位键推给父节点
递归回溯
父节点也可能被挤满,沿路径继续分裂;根满则树长高一层
往字典加新词对应 →B-树插入

翻页对应定位;撕两页对应分裂中位上提;目录也满继续撕对应递归

2第 2 页 · 插入算法的三步曲

插入算法流程图

从顶部开始向下追:先找位,再插入;满则分裂上提,一路向上直至根。

图解渲染中…
a3判断当前节点键数是否小于 m-1a9把分裂节点的中间键提到父节点a12分裂一路向上传,可能波及根a14B-树唯一长高的情形
3第 3 页 · 插入算法流程图

分裂的触发条件

三步曲最后一步是'必要时分裂'——什么时候算'必要'?答:目标节点已持有 m-1 个键,再插就突破上限的瞬间。

节点已满
目标节点恰好持有 m-1 个键,再塞一个就超载
m阶上界
m阶B-树节点最多 m-1 个键,达到就必须分裂
中键上提
分裂时把中间键送到父节点,左右两半各成新节点
向上递归
若父节点也被填满,继续上提,可能一路到根节点
公交车满员分车对应 →B-树节点分裂

满员后再来人调度新车,中位乘客转调度站对应中键上提

4第 4 页 · 分裂的触发条件

分裂的详细过程

箭头方向即分裂执行顺序;中位数上移既是分裂成果,也是触发父节点判断的条件。

图解渲染中…
a12t-1 是节点键数上限,t 为最小度数d1左侧原节点保留前 t-1 个键e1右侧新建节点装后 t-1 个键f1中位数上移后父节点是否也溢出
5第 5 页 · 分裂的详细过程

分裂操作的伪代码

c

这段可链接的 C 实现展示 split_child:搬移满孩子的右半内容,再上提中位键并腾出父节点槽位。

代码高亮加载中…

高亮行先创建右节点、复制右半键/孩子并缩短左节点;随后右移父节点内容、插入新右孩子,再把左节点第 t−1 个键上提。前提是 t≥2、child[i] 已满且父节点未满。

6第 6 页 · 分裂操作的伪代码

分裂后的节点性质

分裂完成后,新生成的两个节点并不是「随便分一半」——它们的结构遵循严格的规则。我们来看看,分裂后左右节点各自拿到多少关键字、又能挂多少子树,以及这些数字之间藏着什么不变量。

关键字数:左右各 t-1 个
原 2t-1 个关键字均分一半,中位数上移至父节点
子树数:左右各 t 棵
每个新节点都恰好达到 B-树的最小度数 t
中位关键字升入父节点
它充当左右子树的分隔,并参与父节点的查找
仅当根节点分裂时树增高
普通节点的分裂只把压力上传,高度本身不会增长
B-树不变量始终保持
每个非根节点关键字数 ≥ t-1,树保持平衡
公司拆分为两个部门对应 →B-树节点分裂

原团队一半进新部门,组长升任上级管两边,两边都达到最小规模

(t1)+1+(t1)=2t1(中位上移,左右各取一半)(t-1)+1+(t-1)=2t-1\quad(\text{中位上移,左右各取一半})
7第 7 页 · 分裂后的节点性质

为什么需要再分裂

上一页我们把满的子节点一分为二,中间键向上推到父节点。但这个「上推」动作可能撞上已经装满的父节点——这就是再分裂的根源。

分裂的副作用
子节点分裂后,中间键必须上抛到父节点
父满阻塞
若父节点键数已达 m−1,新键无处安放
再分裂父节点
父节点同样一分为二,中间键继续上抛
向上递归传播
分裂沿路径上溯,直到某一层有空位
根满则长高
根也满时分裂根,B-树高度增加一层
衣柜层层叠放对应 →B-树节点分裂

下层满了往上塞,父层也满就再加一层柜子

8第 8 页 · 为什么需要再分裂

连锁分裂的递归过程

从下往上看:插入触发叶子分裂,中间键上浮到父节点;父节点若也满了,分裂继续向上传播,直到根节点。

图解渲染中…
a2判断节点键数是否已达上限 m-1a6递归判断:父节点是否也满了a11只有根节点分裂时,树的高度才增加
9第 9 页 · 连锁分裂的递归过程

连锁分裂的复杂度分析

上一页我们看到分裂会沿着路径一路向上传,那它到底会传多远?我们先记住一个事实:B-树的高度天然就是 O(log_m n),这把连锁分裂的「天花板」钉死了。

分裂上限即树高
分裂只能沿路径向上走,最多走到根,所以最多分裂 h 层
每层代价 O(1)
单次分裂只搬走一半节点,和 m、n 无关,是常数操作
根分裂特殊情形
根是唯一没有父节点的节点;根满时分裂会让树长高一层
插入总代价 O(log_m n)
遍历路径 O(log_m n),分裂最多同量级层,总复杂度就是对数级
往信封袋子里塞文件对应 →B-树插入的连锁分裂

袋子塞满就换大一号袋子往上交,最坏情况每个袋子都要换,但袋子层数本来就是对数个

10第 10 页 · 连锁分裂的复杂度分析

根节点分裂的特殊性

上页连锁分裂一路把 key 上推到根,发现根也满了——可根没有父亲,递归到哪里结束?

根无父可推
分裂产生的 key 找不到父节点,必须自立门户
新根独立生成
旧根被一分为二,中间 key 单独成为新根节点
树只从根长高
B-树高度仅在根分裂时 +1,其他分裂都不变高度
根分裂无需递归
根分裂直接结束,不像内部节点要一路向上传递
公司本身裂变对应 →根节点分裂

分公司裂变只需把领导上报给公司;公司自己裂变则要新设集团总部

11第 11 页 · 根节点分裂的特殊性

分裂到根的完整过程

从满根出发,沿因果链看根如何被劈开、新根如何诞生。

图解渲染中…
a1根节点键数已达上限2t-1a4第t个键(中位数)准备上移a6该键成为新根的唯一键
12第 12 页 · 分裂到根的完整过程

B-树高度增长的规律

上一页我们完整看了根分裂的过程。现在跳出细节看整体:B-树变高是一件罕见的事,但一旦发生,所有叶子会集体下沉一层;而插入的均摊代价并不会因此恶化。

唯一触发点
只有根节点满了并执行分裂时,树高才 +1;非根分裂只是横向调整
稀有事件
根必须先累积到 2t-1 个关键字才会触发;子节点分裂只会把关键字往根推
整树级联
树高一旦增加,所有叶子深度同步 +1 只是指针路径集体调整
均摊 O(log n)
根分裂极稀少,分摊后单次插入代价仍为 O(log n) 不变
公司加盖新楼层对应 →B-树长高

只有 CEO 办公室(根)挤不下才盖新楼,部门扩张只换大办公室不加层

h1+logt ⁣(n+12)h \leq 1 + \log_t\!\left(\frac{n+1}{2}\right)
13第 13 页 · B-树高度增长的规律

实例1:首次分裂触发

m=3 的 B-树从空树建起,第三次插入时首次触碰上限,分裂由此触发。

1
插入第1个关键字
空树根节点获得第1个关键字,节点内只有1个元素
2
插入第2个关键字
根节点积累到2个关键字,刚好等于 m-1=2 的上限
3
插入第3个关键字
若原位放入,根节点将持有3个关键字,超出 m-1=2 的允许
4
触发首次分裂
取中间关键字上移成为新根,原节点一分为二形成左右子树
14第 14 页 · 实例1:首次分裂触发

实例2:连锁分裂

插入遇满节点要先分裂;若上层也满,分裂会一路向上传,直到根。

1
下行定位
从根往下找叶子,沿途预判每个节点是否已满
2
底层分裂
目标叶节点已达 2t−1 键,先分裂再插入,中间键上推
3
上层也满
父节点同样已满,无法直接接收推上来的键
4
被迫再分裂
父节点先分裂再容纳,键继续向上推;祖父若满则照做
5
传到根则长高
分裂沿祖先链逐层上传;根若也满则根分裂,树高 +1
15第 15 页 · 实例2:连锁分裂

实例3:分裂到根

上次连锁到根就停,这次把根也拆开——这是 B 树长高的唯一方式。

1
子节点上推
子叶继续分裂,中间键沿父链一路送到根
2
根也满了
根已有 2t−1 个键,再收一个就违反上界
3
分裂根节点
以根的中间键为界,把 2t 个键一分为二
4
新根诞生
原根中间键单独浮起,成为新的根节点
5
树高+1
整棵树高度增加一层,旧根降级为新根两个孩子
16第 16 页 · 实例3:分裂到根

实例4:复杂插入序列

随机序列会让多种分裂情形串成一条完整路径,从无分裂一路走到根分裂。

1
无分裂阶段
前几次插入节点未满,键直接落位,未触发任何分裂
2
首次叶分裂
节点满后再插,叶节点分裂,中间键上提至父节点
3
父节点蓄满
上提键挤满父节点,下次插入令父节点濒临溢出
4
连锁分裂
因子插入触发父节点分裂,分裂递归向上传播
5
根节点分裂
分裂冲至根,旧根被一分为二并生成新的更高根
6
高度增长完成
新根确立,B-树整体高度+1,所有性质重新满足
17第 17 页 · 实例4:复杂插入序列

自测题

检验插入算法核心理解

自测题
检验插入算法核心理解
18第 18 页 · 自测题

核心要点总结

  • 分裂是预防性的:满之前就主动拆,而非满了才被动修
  • 分裂沿祖先链向上递归传播,单次插入可能引发级联
  • 高度仅在根分裂时 +1,阶跃式增长,远慢于 BST
  • 分裂是保形变换,所有 B-树不变量在变换前后恒成立
延伸主题:B-树的删除与再分配B-树与红黑树的结构等价性B+树:分裂策略的工程变体
19第 19 页 · 核心要点总结

课后思考

先自己想,再看参考答案——三个层次,逐步深入。

1为什么B-树的分裂要在节点溢出时就立即处理,而不是累积到一起再调整?

参考答案若延迟分裂,下层节点键数将超过 m-1,破坏B-树定义的不变量,且查找路径也无法保证正确。

2如果把B-树的最小度数 t 设为2,和设为3相比,查找效率与存储利用率会怎么变化?

参考答案t=2时节点最少仅1键,利用率下限约50%,树高更大、磁盘I/O更多;t 越大利用率越高(约 2t/(2t+1)),但单节点变大。

3B-树的插入是自底向上分裂,那么B+树插入时的分裂逻辑会有什么差异?为什么?

参考答案B+树分裂需把键复制到两侧节点;分裂后同一键同时存于父节点与子节点,出现冗余存储——因为B+树内部节点也要靠键做索引。

20第 20 页 · 课后思考