B+树、B树和红黑树介绍
红黑树(Red-Black Tree)
-
特点:
- 本质上是一种自平衡的二叉查找树(Binary Search Tree)。
- 每个节点要么是红色,要么是黑色。
- 通过一套严格的着色规则来维持树的平衡:
- 每个节点非红即黑。
- 根节点是黑色的。
- 所有叶子节点(NIL节点,空节点)都是黑色的。
- 如果一个节点是红色的,则它的两个子节点必须是黑色的(不能有两个连续的红色节点)。
- 从任一节点到其每个叶子节点的所有路径都包含相同数目的黑色节点(黑高)。
- 插入和删除操作后,可能会破坏这些规则,此时通过旋转(Rotation)和颜色翻转(Color-Flipping)等操作来重新调整树的结构,使其恢复平衡。
- 树的高度近似于
log N(N是节点数),保证了查找、插入、删除的时间复杂度都是O(log N)。
-
适用场景:
- 主要用于内存中的数据结构。因为它的平衡调整操作(旋转和变色)比较频繁且细粒度,这些操作在内存中非常快。
- 例如,Java中的
TreeMap,TreeSet;C++ STL中的map,set;Linux内核中的完全公平调度器(CFS)和虚拟内存管理等。
B树(B-Tree)
-
特点:
- 是一种多路(multi-way)平衡查找树,不是二叉树。每个节点可以有多个子节点。
- 一个m阶的B树,每个非叶子节点最多有m个子节点,最少有
ceil(m/2)个子节点。 - 所有叶子节点都在同一层级,保持了树的平衡。
- 数据(或指向数据的指针)存储在所有的节点中,包括内部节点和叶子节点。
- 每个节点可以存储多个键,节点内的键是有序的。
- 节点的大小通常设计为与磁盘块(或页)的大小相匹配,以优化磁盘I/O。
-
适用场景:
- 主要用于磁盘等外部存储设备上的数据索引。因为磁盘I/O的成本远高于内存访问,B树的多路特性使得树的高度非常低(O(log_m N)),从而大大减少了查找数据时所需的磁盘I/O次数。
- 例如,一些文件系统和非关系型数据库(NoSQL)可能会使用B树或其变体。
B+树(B+-Tree)
-
特点:
- 是B树的一种变体,同样是多路平衡查找树。
- 与B树最核心的区别在于数据存储方式:
- 所有的数据记录都只存储在叶子节点上。
- 内部节点(非叶子节点)只存储键(作为索引)和指向子节点的指针,不存储任何与键关联的数据。
- 所有叶子节点通过指针串联起来,形成一个有序的双向(或单向)链表。
- 内部节点中的键会冗余地出现在其子树的叶子节点中(作为索引或边界)。
-
适用场景:
- 绝大多数关系型数据库的索引结构,特别是像MySQL的InnoDB存储引擎。
- 文件系统索引。
| 特性 | B树 | B+树 | 红黑树 |
|---|---|---|---|
| 基本结构 | 多路平衡查找树 | B树的变种,多路平衡查找树 | 近似平衡的二叉查找树 |
| 数据存储 | 所有节点(内部节点和叶子节点)都存储关键字和数据。 | 只有叶子节点存储数据,内部节点仅存储关键字作为索引。 | 所有节点都存储关键字和数据。 |
| 范围查询 | 效率较低,需要进行中序遍历。 | 效率极高,只需在叶子节点的链表上进行遍历。 | 效率较低,需要中序遍历。 |
| 磁盘I/O | 树的高度相对较低,I/O次数少。但内部节点较大,占用更多磁盘空间。 | 树的高度通常比B树更低,I/O次数更少,对磁盘更友好。 | 树的高度相对较高,I/O操作次数多,不适合外存存储。 |
| 查询效率 | 不稳定,最好的情况可能在根节点直接命中,最坏情况需要查找到叶子节点。 | 非常稳定,任何查找都必须到达叶子层。 | 效率稳定,查找、插入、删除的平均和最坏时间复杂度均为O(log n)。 |
| 应用场景 | 数据库和文件系统。 | 绝大多数关系型数据库的索引(如MySQL InnoDB)、文件系统。 | 内存中的数据结构,如C++ STL中的map、set,Java中的TreeMap、TreeSet。 |
| 平衡策略 | 通过节点的分裂和合并来维持高度的绝对平衡。 | 与B树类似,通过节点分裂和合并维持平衡。 | 通过节点颜色的变换和树的旋转来维持近似平衡。 |
- B树和B+树由于其矮胖的结构,非常适合需要大量I/O操作的外存(磁盘)场景。它们通过一次读取一整个节点(磁盘页)的数据来减少昂贵的磁盘寻道次数。
- B+树相较于B树,在范围查询和顺序访问方面有巨大优势,并且通常有更低的树高,是数据库索引更优的选择。
- 红黑树是一种更通用的内存数据结构。 它的平衡调整开销相对较低,能够在频繁的插入和删除操作中保持高效的性能,因此广泛应用于实现内存中的关联数组或集合。