B树的删除操作
概述
B树的删除操作是B树最复杂的操作,通过借关键字和合并来修复下溢。与B树的插入操作的”提前分裂”策略类似,删除采用"提前保障"策略:在从根向叶子遍历的过程中,确保当前节点在下降到子节点之前,目标子节点至少有 个关键字,从而避免回溯。
核心思想
删除操作的关键挑战:直接删除关键字可能导致节点关键字数低于最小值 (即发生下溢)。为此,需要在下降过程中提前确保每个被访问的节点都有足够的关键字。
三种删除情况
情况1:关键字 在叶子节点 中
直接从 中删除 。由于 是叶子,删除不影响其他节点。
前提
在下降到 之前,已经通过提前保障策略确保 至少有 个关键字,因此删除后 至少有 个关键字,不会下溢。
情况2:关键字 在内部节点 中
需要找到替代关键字来填充 的位置,然后递归删除替代关键字:
-
情况2a: 的前驱子节点 ( 左侧的子树)至少有 个关键字
- 找到 中最大的关键字 (即 的直接前驱)
- 用 替换 中的
- 递归地从 中删除
-
情况2b: 的后继子节点 ( 右侧的子树)至少有 个关键字
- 找到 中最小的关键字 (即 的直接后继)
- 用 替换 中的
- 递归地从 中删除
-
情况2c: 和 都只有 个关键字
- 将 与 和 合并为一个节点
- 下移到合并后的节点中
- 递归地从合并后的节点中删除
情况3:关键字 不在当前内部节点 中
确定 应该在的子节点 。在下降到 之前,需要确保 至少有 个关键字:
-
情况3a: 只有 个关键字,但相邻兄弟 或 至少有 个关键字
- 借关键字(旋转):从兄弟节点借一个关键字通过父节点转移给
- 例如, 有 个关键字时:
- 将 中 和 之间的关键字 下移到 的开头
- 将 的最后一个关键字上移到 中 的位置
- 将 的最后一个子指针转移到
- 借关键字后, 有 个关键字,可以安全下降
-
情况3b: 和所有相邻兄弟都只有 个关键字
- 合并:将 与一个兄弟节点合并
- 将 中分隔两者的关键字下移到合并后的节点中
- 合并后节点有 个关键字
- 如果 是根且变空,合并后的节点成为新根,树高度减
修复操作详解
借关键字(旋转)
借关键字操作是合并的逆操作,与B树的插入操作中的分裂操作具有对称性:
| 操作 | 方向 | 触发条件 |
|---|---|---|
| 分裂(插入) | 节点 父节点(上移中间关键字) | 节点满(个关键字) |
| 借关键字(删除) | 父节点/兄弟 节点(下移关键字) | 节点下溢(个关键字) |
合并
合并操作是分裂的逆操作:
- 分裂:一个 关键字节点 两个 关键字节点 + 1 个关键字上移
- 合并:两个 关键字节点 + 1 个关键字下移 一个 关键字节点
核心性质
1. 提前保障策略
与插入的提前分裂类似,删除采用提前保障策略:
- 向下遍历时:如果当前节点的目标子节点只有 个关键字,立即通过借关键字或合并来修复
- 保证:到达叶子节点时,该节点至少有 个关键字,删除后至少 个,不会下溢
- 结果:只需单次向下遍历,无需回溯
2. 复杂度分析
- 磁盘I/O: 次
- 向下遍历: 次 DISK-READ
- 借关键字/合并:每次 次 DISK-READ 和 DISK-WRITE
- 总修复操作次数不超过
- CPU时间:
3. 树高度变化
- 删除可能导致树高度减少(当根的子节点合并导致根变空时)
- 这与插入导致树高度增加恰好相反
4. 与插入操作的对称性
| 插入操作 | 删除操作 |
|---|---|
| 节点溢出( 个关键字) | 节点下溢( 个关键字) |
| 分裂(split) | 合并(merge) |
| 树可能长高 | 树可能变矮 |
| 提前分裂 | 提前保障 |