
大家好,很高兴又和大家见面啦!!! 在上一篇内容中,我们深入探讨了 红黑树(RBT)的插入操作,理解了它如何通过颜色调整与旋转操作维护“适度平衡”。现在,我们站在两个关键问题的交汇点:
今天,我们将带着对红黑树的理解,自然过渡到 多路平衡查找树(B树) 的世界。从二叉到多叉,从内存到磁盘,让我们一同探索数据结构如何为不同场景量身定制解决方案!
经过前面的学习,我们以及对红黑树及其插入操作有了一定的认知。对于红黑树而言,其删除操作相比于插入操作会更加的复杂,不过现阶段我们并不需要对其进行深入的探讨,仅作简单的了解即可。
红黑树的删除操作我们需要了解以下内容:
简单的说就是红黑树在进行删除操作时,若该删除操作破坏了 RBT 的特性,则需要进行相应的调整使其恢复为一棵 RBT ; 删除操作的具体内容,我们会在今后的学习中再进行详细的介绍,这里就不再展开;
RBT 的时间复杂度与 AVL 的时间复杂度一致,均为 O(\log_2 N) 既然二者的时间复杂度一致,那为什么还要有 RBT 这种数据结构呢? 在前面我们也有过介绍,这是因为 AVL 的 高度平衡 的严格规定,这就导致了 插入 与 删除 操作十分容易破坏 AVL 的 平衡特性 ,进而导致了在执行这些操作时,需要频繁的进行 平衡调整 操作; 而 RBT 的 适度平衡 的相对松弛规定,就保证了这些操作不会那么容易破坏 RBT 的 红黑特性 ,从而大幅度的降低了 平衡调整 操作的频率; 因此在需要频繁进行查找操作的情况下,使用 AVL 会更加的合适,而对于频繁进行 插入、删除 的动态查找中,使用 RBT 会更加合适;
尽管 RBT 通过其独特的着色规则和适度的平衡性,在内存中的动态数据管理上表现出色,但当我们面对海量数据、需要依赖磁盘等外部存储时,它的二叉树形态可能导致树高较大,进而引发频繁的I/O操作,成为性能瓶颈。 为了解决这类问题,我们引入了 多路查找树 的概念。与二叉查找树每个节点最多只有两个分支不同,多路查找树的一个节点可以拥有多个子节点,从而显著降低树的高度。 B树正是为了磁盘等存储设备而设计的一种高效、平衡的多路查找树。接下来,我们将从多路查找树的基本思想出发,逐步展开对B树核心概念的学习。
多路查找树(Multiway Search Tree)是一种重要的树形数据结构,它突破了二叉查找树每个节点只能有一个关键字和最多两个子节点的限制,专为需要高效管理大规模数据,特别是涉及外部存储(如磁盘)的场景而设计。
这里我们需要注意,多路查找树 不能够缩写为 MST ,这是因为我们在学习 图 时,有介绍过一种 MST ,其全称是 Minimum Spanning Tree (最小生成树),这里为了避免歧义,因此我们不会将其称为 MST
一棵 m叉查找树 可以是一棵空树,也可以时满足以下特性的 m叉树:
flowchart TB
subgraph PS[说明]
word[关键字<br>可以递增:key1 < key2 < ... < keym - 1<br>可以递减: key1 > key2 > ... > keym - 1<br>数量最多为m - 1]
sub[子树<br>数量取决于结点中关键字数量<br>m - 1 个关键字可以有 m 棵子树]
fail[失败结点<br>表示查找失败, 为树中不存在的空结点]
end
subgraph Tree[m叉查找树]
direction TB
a[key1, key2, ..., keym-1]
b[子树1]
c[子树2]
d[...]
e[子树m]
a--->b--->b1[NULL<br>失败结点]
a--->c--->c1[NULL<br>失败结点]
a--->d--->d1[NULL<br>失败结点]
a--->e--->e1[NULL<br>失败结点]
end
Tree--->PS在这之前,我们学习的 二叉查找树 (Binary Search Tree, BST)多路查找树 是两种重要的树形搜索结构。 当数据量巨大且涉及磁盘存取时,多路查找树(如B树)通过允许每个节点拥有多个子节点,能够有效降低树高,从而显著减少I/O操作次数,克服了 二叉查找树 在这种场景下的局限性。
在 BST 中,各结点的值满足 左子树 <<
因此我们每一次的查找操作都是在符合条件的范围内查找目标关键字;
flowchart TB
a[key1]
b[key2]
c[key3]
a--->b
a--->c
b--->b1[负无穷, key2]
b--->b2[key2, key1]
c--->c1[key1, key3]
c--->c2[key3, 正无穷]当我们将 BST 中每个结点存储的关键字数量由一个扩展到 m 个时,二叉查找树 就被拓展到了 m叉查找树;
flowchart TB
a[key1, key2, ..., keym-1]
a--->a1[负无穷, key1]
a--->a2[key1, key2]
a--->a4[...]
a--->a5[keym-2, keym - 1]
a--->a6[keym - 1, 正无穷]可以看到,在 m叉查找树 中,每个结点可以含有多个 关键字 以及多个 子结点
当 m叉查找树 的每个结点都只存储一个关键字时,此时的 m叉查找树 就会退化为一棵 BST; 在这种情况下,若关键字的总数不变,此时的 BST 就会变的细长,进而导致查找效率降低; 如一棵 5叉查找树 中总共有16个关键字:
flowchart TB
a[5, 11, 22, 36]
b[1, 3]
c[6, 8, 9]
d[13, 15]
e[30, 35]
f[40, 42, 45]
a--->b
a--->c
a--->d
a--->e
a--->f当其在关键字的总数不变的情况下,5叉查找树 退化为了一棵 BST:
flowchart TB
a1[3]
b1[1]
a1--->b1
b2[5]
a1--->b2
a2[11]
c2[8]
a2--->c2
c1[6]
c2--->c1
c1--->a1
c1--->c1_[NULL]
c3[9]
c2--->c3
d1[13]
a2--->d1
d1--->d1_[NULL]
d2[15]
d1--->d2
a3[22]
a3--->a2
a4[36]
e1[30]
a4--->e1
e2[35]
a4--->e2
f1[40]
a3--->f1
f1--->a4
f2[42]
f1--->f2
f2--->f2_[NULL]
f3[45]
f2--->f3可以看到,此时我们以 22 作为 BST 的根结点时,树高从 5叉查找树 的 h_5 = 2 变成了 h_2 = 6 当然这只是其中一种情况还算是比较好的退化,若是以 5 作为根,那么我们得到的 BST 的高度还会继续增加; 在介绍 BST 时,我们就有提到过这一问题:
同理,当 5叉查找树 在退化过程中,关键字序列接近有序时,5叉查找树 同样也会退化为链表,从而大大降低查找的效率; 因此为了保证查找的效率,m叉查找树 规定:
如 5叉查找树 除了根结点外,其他结点至少含有 \lceil \frac{5}{2} \rceil = \lceil 2.5 \rceil = 3 个分支,至少含有 2 个关键字
flowchart TB
root[key,_,_,_<br>最多4个关键字<br>最少1个关键字]
lchild[key1, key2,_,_<br>最多4个关键字<br>最少2个关键字]
rchild[key1, key2,_,_<br>最多4个关键字<br>最少2个关键字]
root--->lchild
root--->rchild
l1[key1, key2,_,_<br>最多4个关键字<br>最少2个关键字]
l2[key1, key2,_,_<br>最多4个关键字<br>最少2个关键字]
l3[key1, key2,_,_<br>最多4个关键字<br>最少2个关键字]
lchild--->l1
lchild--->l2
lchild--->l3
r1[key1, key2,_,_<br>最多4个关键字<br>最少2个关键字]
r2[key1, key2,_,_<br>最多4个关键字<br>最少2个关键字]
r3[key1, key2,_,_<br>最多4个关键字<br>最少2个关键字]
rchild--->r1
rchild--->r2
rchild--->r3在树形结构中,其查找效率与树的高度之间成正比:
二者之间可以用关系式:S = O(h) 表示,而对于一棵平衡树,其树高与结点之间则是成对数关系:h = O(\log N) ; 这也就是说,对于一棵平衡树,其查找效率与结点之间的关系可以表示为:S = O(\log N) ; 这个结论在 m叉查找树 中也同样适用,因此当 m叉查找树 的某一结点的个子树之间的高度相差太大,从而导致该子树失衡时,m叉查找树 的查找效率同样也会降低; 因此为了保证 m叉查找树 的查找效率,我们在创建一棵 m叉查找树 时,应尽可能的保持树的平衡状态;
多路平衡查找树是一种重要的数据结构,它允许每个节点拥有多于两个子节点,并通过自平衡机制维持高效查找性能。 多路平衡查找树(特别是B树)的设计主要是为了高效管理无法全部装入内存的大规模数据集,尤其是涉及磁盘等外部存储的设备。
多路平衡查找树通过一套严格的规则在插入和删除操作时维持其平衡特性:
多路平衡查找树是一个概念家族,其中包括5类平衡查找树:
B树 是一种 绝对平衡 的 自平衡多路查找树,所谓的 m阶B树 指所有结点的 平衡因子均等于 0 的 m路平衡查找树。
一棵 m阶B树 可以是一棵空树,也可以是一棵满足以下特性的 m叉树:
flowchart TB
a[n]
b[P0]
c[K1]
d[P1]
e[K2]
f[...]
g[Kn]
h[Pn]其中,n(\lceil \frac{m}{2} \rceil - 1 \leq n \leq m - 1) 为结点中关键字的个数;K_i(i= 1, 2, \cdots, n) 为结点的 关键字,且满足 K_1 < K_2 < \cdots < k_nP_i(i= 0, 1, \cdots, n) 为指向子树根结点的指针,且指针 P_{i - 1} 所指子树中所有结点的关键字均小于 K_i,P_i 所指子树中所有结点的关键字均大于 K_i;
这里我们以一棵 5 阶 B树 为例,进一步理解上述性质:
flowchart TB
subgraph R[根结点]
direction TB
n[n = 1]
p0[P0]
key[key1 = 22]
p1[P1]
end
subgraph r[子树2]
direction TB
nr[n = 2]
pr0[P0]
keyr1[key1 = 36]
pr1[P1]
keyr2[key2 = 45]
pr2[P2]
keyr3[key3 = 50]
pr3[P3]
keyr4[key4 = 56]
pr4[P4]
end
p1--->r
pr0--->n4[NULL]
pr1--->n5[NULL]
pr2--->n6[NULL]
pr3 ---> n7[NULL]
pr4 ---> n8[NULL]
subgraph l[子树1]
direction TB
nl[n = 2]
pl0[P0]
keyl1[key1 = 5]
pl1[P1]
keyl2[key2 = 11]
pl2[P2]
end
p0--->l
pl0 ---> n1[NULL]
pl1 ---> n2[NULL]
pl2 ---> n3[NULL]在这棵 5阶B树 中,各结点的子树、关键字分别满足以下内容:
通过本篇的学习,我们系统性地探索了从红黑树的删除操作到B树基本概念的完整知识路径。以下是本次内容的核心要点回顾: 📚 核心知识总结
知识模块 | 核心要点 | 实际意义 |
|---|---|---|
红黑树删除 | 遵循BST删除规则,删除后可能需颜色调整与旋转操作以恢复红黑特性 | 维持了“适度平衡”,在频繁增删场景下性能优于AVL树 |
红黑树性能 | 时间复杂度与AVL树相同($O(\log_2N)$),但调整频率更低,适合动态数据集 | 在插入、删除操作频繁的场景中更具优势 |
多路查找树 | 突破二叉限制,单个节点可含多个关键字与子树,显著降低树高 | 为处理海量数据奠定基础 |
B树核心特性 | 绝对平衡的多路查找树,所有叶子节点位于同一层,通过节点分裂/合并维持平衡 | 专为磁盘等外部存储设计,极大减少I/O操作次数 |
💡 学习路径价值 从红黑树到B树的学习路径,展现了数据结构设计如何针对不同应用场景(内存计算vs.磁盘存储)进行优化。
这两类结构各有其最佳适用场景,理解它们的设计哲学与优缺点,将为我们在实际工程中选择合适的数据结构提供坚实依据。 互动与分享
感谢您的耐心阅读! 关注博主,不错过更多技术干货。我们下一篇再见!