B, B+, 红黑树记录
都是用于加快查找的平衡树,应用场合和性质有所不同,需要理解使用他们的原因,以及他们各种奇怪的自平衡的方式。
B树
B,B+树主要用于数据库、文件系统索引。具体如何是实现?
主要长这样,一个节点存储的有多个数据和子节点索引。
b树对于每个节点中的数据和子节点索引的数量有如下限制
假设该B树为
t
阶,也就是每个节点中子节点数量最大值为t
,t
取决于磁盘块大小,因为读取磁盘的时候是一块一块读取的,所以一次IO读取一个b树节点,该节点最大限制就是磁盘块的限制。
- 根节点至少两个子节点
- 每个中间节点都包含k-1个数据和k个子节点,其中 m/2 <= k <= m
- 每个叶子节点都包含k-1个数据,其中 m/2 <= k <= m
- (以上两个 m / 2 都是向上取整)
插入和删除操作也都是围绕这个规则进行。
- 如果插入某个数据后该节点的数据或者子节点数量超出限制,则将该节点中间值提出来作为父节点放到上一层,剩下的部分分成两半。当然,如果上一层依然满了,继续向上,最后如果直到根节点还要往上,那B树就会增加一层。
- 如果删除数据后,某个节点相关的数量低于限制,则会查看左右兄弟节点数据是否足够(大于最低限制),足够的话将父节点中的一个元素转移到当前节点,再把兄弟节点的最小(或者最大,取决于在左兄弟还是右兄弟)上升到父节点下移数据的位置。如果左右兄弟都不够,可以先下移父节点的数据,然后和兄弟合并。具体的可以看这个文章ref
优缺点
优点
- 一个节点能存储更多数据和节点信息,树的高度下降,查询次数降低,降低IO次数。
缺点
- 维护复杂,在内存中查询的速度不如红黑树和哈希
- 不适合范围查询,因为范围连续的数据可能在B树的不同层级,需要中序遍历(因为中序遍历是有序的序列)。范围、遍历不如B+树
关键就在于B树经常用于装不进内存中的数据查询,比如数据库,因为数据量太大,无法把整个数据放进内存,所以不适合用哈希和红黑树,更适合使用B树,一次可以只加载一个节点进入内存进行查询。他一个节点存储的数据比哈希和红黑树多很多,所以降低高度,减少IO次数。
B+树
B+树在B树的基础上,每个非叶子节点只存子节点索引和关键字,不存数据。叶子节点之间相连成链表。
长这样。
优点
- 查询效率稳定,因为数据都在叶子节点。
- 范围查询高效,因为是在链表上范围查询。
- 适合读取。
缺点
- 不适合随机写入,因为B+树需要频繁分裂合并,造成效率下降(B树也是)
一些解决方式,(GPT)
对于数据库中频繁的随机写入操作,传统的B+树可能不是最佳选择,因为它需要频繁地进行分裂和合并操作,从而降低性能。以下是一些适合处理频繁随机写入的数据结构或技术:
- 哈希表: 哈希表适用于快速随机访问,因为它可以直接计算数据的哈希值并将数据插入或查找。然而,哈希表通常不支持范围查询。
- LSM树(Log-Structured Merge Tree): LSM树是一种特殊的数据结构,优化了随机写入操作。它将写入操作追加到一个新的磁盘文件,然后定期将这些文件进行合并,从而减少随机写入的开销。LSM树广泛用于分布式数据库系统,如HBase和Cassandra。
- B树的变种: 在某些情况下,可以使用B树的变种来处理频繁的随机写入。例如,B树的变种B*-Tree和Fractal Tree可以更好地处理高并发写入操作,但它们仍然需要权衡查询性能。
- 内存数据库: 如果数据可以完全存储在内存中,并且随机写入是主要操作,那么内存数据库可能是一个更好的选择,因为内存中的数据结构通常对随机写入操作更加友好。
- 分区和分片: 将数据库表分成多个分区或分片,每个分区/分片独立管理,可以减轻随机写入操作对整个数据库的影响。
红黑树
红黑树也是一种类平衡二叉树,“类”是因为他没有定死的平衡因子,比如AVL平衡因子是1,即要求左右子树高度差不能超过1.
红黑树有五条节点限制,这些限制的结果就是左右子树最高不会超过最短的两倍。
此文详细介绍了五条规则和调整细节ref
和AVL相比,有什么不同
- 因为高度限制比较宽松,所以同样数据最大高度更高
- 因为限制宽松所以红黑树用于调整的时间更短,当然,增删查都是log n
- 红黑树:其中添加、删除都仅需 O(1) 次旋转调整
- AVL树:其中添加仅需 O(1) 次旋转调整、删除最多需要 O(logn) 次旋转调整
如何选择
如果查询比较多,用AVL,如果增删查都差不多,用红黑树。
实际中红黑树用的更多。比如java中TreeMap, TreeSet