数据结构 - B-树 B-Tree

随着数据规模的增长和存储介质的限制,传统的二叉树结构逐渐显现出其不足:在外存环境下,频繁的 I/O 操作严重影响性能。为了解决这一问题,B-树(Balanced Tree)被发明,用于高效管理外存中的数据结构。

B-树是一种多路平衡搜索树,特别适合大规模数据存储和检索,其高效的节点分裂与合并机制使其成为数据库和文件系统的核心数据结构之一。本文将深入讲解 B-树的定义、操作及其实现。

1. 什么是B-树

B-树是一种多路平衡搜索树,具有以下特性:

  1. 多路性
    B-树的每个节点可以拥有多个子节点,而不是像二叉树那样限制为两个。这种结构减少了树的高度,从而减少了 I/O 次数。
  2. 节点平衡性
    • 树的所有叶子节点位于同一层,保证查询时间的稳定性。
    • 每个非叶子节点最多包含 \(m-1\) 个关键字(即 \(m-1\) 个子节点),且至少包含 \(\lceil \frac{m}{2} \rceil - 1\) 个关键字(其中 \(m\) 是B-树的)。
  3. 自平衡性
    插入或删除节点后,B-树通过节点分裂或合并保持平衡,确保树的高度接近 \(O(\log{n})\)

2. B-树的基本操作

一个典型的 B-树节点由以下部分组成:

  • 关键字数组:用于存储有序的关键字(如 [K1, K2, ..., Kn])。
  • 子节点指针数组:每个节点维护与其关键字相关的子节点指针(如 [P1, P2, ..., Pn+1])。这些指针用于连接子树。

2.1 插入操作

插入操作分为以下几步:

  1. 定位插入位置
    按搜索路径找到叶子节点插入关键字。
  2. 插入关键字
    将关键字插入节点中的相应位置。
  3. 节点分裂(Split)
    如果节点关键字数量超过 \(m-1\),则将中间关键字上移到父节点,节点分裂为两个子节点。向上递归地传递这一过程。

示例:插入关键字到 3 阶B-树

        [20]
       /    \
   [10]    [30, 40]

插入关键字 50:

  1. 定位到叶子节点 [30, 40];
  2. 插入关键字后叶节点变为 [30, 40, 50];
  3. 节点分裂,中间关键字 40 上移。
        [20, 40]
       /   |    \
   [10] [30]   [50]

注意,如果一直分裂到根节点,那么就需要创建一个新的根节点。

2.2 删除操作

删除关键字可能导致节点关键字不足(少于 \(\lceil \frac{m}{2} \rceil - 1\)),需要以下操作:

  1. 删除关键字
    • 如果关键字位于叶子节点,直接删除;
    • 如果关键字位于内部节点,用前驱或后继关键字替换后递归删除。
  2. 节点合并(Merge)或借关键字(Redistribute)
    • 若关键字不足,从兄弟节点借一个关键字;
    • 如果兄弟节点关键字也不足,则合并当前节点和兄弟节点。

2.3 查询操作

B-树的搜索操作类似于二叉搜索树:

  1. 从根节点开始,顺序比较关键字;
  2. 如果目标值位于当前节点中,则搜索结束;
  3. 如果不在,则根据值的范围进入相应的子节点。

3. B-树的复杂度分析

  • 时间复杂度:B-树的建树时间复杂度为 \(O(n)\);B-树的高度保持在 \(O(\log_m{n})\),因此查找、插入、删除节点的时间复杂度都是 \(O(\log_m{n})\)
  • 空间复杂度:\(O(n)\)

4. B-树的优势

普通的 BST 结构每个节点只能容纳一个数据,导致树的高度还是偏高,逻辑上相邻的节点可能距离较远。考虑到在磁盘和内存上存储数据的差异:

  • 读写磁盘的速度相比内存读写慢很多;
  • 每次读写磁盘的单位要比读写内存的最小单位大很多。

在磁盘上存储的数据结构应该尽量满足局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用。逻辑上相邻的数据在物理上也尽量存储在一起,这样才能减少读写磁盘的数量。

B-树的一个节点能存储更多的数据,能够降低树的高度,而且能让逻辑上相邻的数据都能尽量存储在物理上也相邻的硬盘空间上,减少磁盘读写次数。

posted @ 2025-02-24 15:05  木杉的园子  阅读(446)  评论(0)    收藏  举报