本文由 程序喵正在路上 原创,CSDN首发!
系列专栏:数据结构与算法
首发时间:2022年12月6日
欢迎关注点赞收藏留言
一以贯之的努力 不得懈怠的人生
B B B 树,又称为多路平衡查找树, B B B 树中所有结点的孩子个数的最大值称为 B B B 树的阶,通常用 m m m 表示,上图即为 5 5 5 阶 B B B 树。一棵 m m m 阶 B B B 树或为空树,或为满足如下特性的 m m m 叉树:
树中每个结点至多有 m m m 棵子树,即至多含有 m − 1 m - 1 m−1 个关键字
若根结点不是终端结点,则至少有两棵子树
除根结点外的所有非叶结点至少有 ⌈ m / 2 ⌉ \lceil m/2 \rceil ⌈m/2⌉ 棵子树,即至少含有 ⌈ m / 2 ⌉ − 1 \lceil m/2 \rceil - 1 ⌈m/2⌉−1 个关键字
所有的叶结点都出现在同一层次上,并且不带信息(可以视为外部结点或类似于折半查找判定树的查找失败结点,实际上这些结点并不存在,指向这些结点的指针为空)
其中, K i ( i = 1 , 2 , . . . , n ) K_i \ (i = 1, 2, ..., n) Ki (i=1,2,...,n) 为结点的关键字,且满足 K 1 < K 2 < . . . < K n K_1 < K_2 < ... < K_n K1<K2<...<Kn; P i ( i = 0 , 1 , . . . , n ) P_i \ (i = 0, 1, ..., n) Pi (i=0,1,...,n) 为指向子树根结点的指针,且指针 P i − 1 P_{i-1} Pi−1 所指子树中所有结点的关键字均小于 K i K_i Ki, P i P_{i} Pi 所指子树中所有结点的关键字均大于 K i K_i Ki, n ( ⌈ m / 2 ⌉ − 1 ≤ n ≤ m − 1 ) n \ (\lceil m/2 \rceil - 1 \leq n \leq m - 1) n (⌈m/2⌉−1≤n≤m−1) 为结点中关键字的个数
m m m 阶 B B B 树的核心特性:
问:含 n n n 个关键字的 m m m 阶 B B B 数,最小高度和最大高度是多少?
最小高度 —— 让每个结点尽可能地满,每个结点都有 m − 1 m-1 m−1 个关键字, m m m 个分叉,则可以得到公式 n ≤ ( m − 1 ) ( 1 + m + m 2 + m 3 + . . . + m h − 1 = m h − 1 n \leq (m - 1)(1 + m + m^2 + m^3 + ... + m^{h - 1} = m^h - 1 n≤(m−1)(1+m+m2+m3+...+mh−1=mh−1,因此 h ≥ l o g m ( n + 1 ) h \geq log_m(n + 1) h≥logm(n+1)
最大高度 —— 让各层的分叉尽可能地少,即根结点只有 2 2 2 个分叉,其他结点只有 ⌈ m / 2 ⌉ \lceil m/2 \rceil ⌈m/2⌉ 个分叉,各层结点至少有:第一层 1 1 1、第二层 2 2 2、第三层 2 ⌈ m / 2 ⌉ . . . 2 \lceil m/2 \rceil ... 2⌈m/2⌉... 第 h h h 层 2 ( ⌈ m / 2 ⌉ ) h − 2 2 (\lceil m/2 \rceil)^{h - 2} 2(⌈m/2⌉)h−2,第 h + 1 h + 1 h+1 层共有叶子结点(失败结点) 2 ( ⌈ m / 2 ⌉ ) h − 1 2 (\lceil m/2 \rceil)^{h - 1} 2(⌈m/2⌉)h−1
n n n 个关键字的 B B B 树必有 n + 1 n + 1 n+1 个叶子结点,则 n + 1 ≥ 2 ( ⌈ m / 2 ⌉ ) h − 1 n + 1 \geq 2 (\lceil m/2 \rceil)^{h - 1} n+1≥2(⌈m/2⌉)h−1,即 h ≤ l o g ⌈ m / 2 ⌉ n + 1 2 + 1 h \leq log_{\lceil m/2 \rceil} \frac{n + 1}{2} + 1 h≤log⌈m/2⌉2n+1+1
我们可以用表格的形式更直观地来计算最大高度:
记 k = ⌈ m / 2 ⌉ k = \lceil m/2 \rceil k=⌈m/2⌉
层数 | 最少结点数 | 最少关键字数 |
---|---|---|
第一层 | 1 1 1 | 1 1 1 |
第二层 | 2 2 2 | 2 ( k − 1 ) 2(k - 1) 2(k−1) |
第三层 | 2 k 2k 2k | 2 k ( k − 1 ) 2k(k - 1) 2k(k−1) |
第四层 | 2 k 2 2k^2 2k2 | 2 k 2 ( k − 1 ) 2k^2(k - 1) 2k2(k−1) |
… | … | … |
第 h h h 层 | 2 k h − 2 2k^{h-2} 2kh−2 | 2 k h − 2 ( k − 1 ) 2k^{h-2}(k - 1) 2kh−2(k−1) |
从表格中我们可以得到: h h h 层的 m m m 阶 B B B 树至少包含关键字总数 1 + 2 ( k − 1 ) ( k 0 + k 1 + k 2 + . . . + k h − 2 = 1 + 2 ( k h − 1 − 1 ) 1 + 2(k -1)(k^0 + k^1 + k^2 + ... + k^{h - 2} = 1 + 2(k^{h - 1} - 1) 1+2(k−1)(k0+k1+k2+...+kh−2=1+2(kh−1−1)
若关键字总数少于这个值,则高度一定小于 h h h,因此 n ≥ 1 + 2 ( k h − 1 − 1 ) n \geq 1 + 2(k^{h - 1} - 1) n≥1+2(kh−1−1),得: h ≤ l o g k n + 1 2 + 1 = l o g ⌈ m / 2 ⌉ n + 1 2 + 1 h \leq log_{k} \frac{n + 1}{2} + 1 = log_{\lceil m/2 \rceil} \frac{n + 1}{2} + 1 h≤logk2n+1+1=log⌈m/2⌉2n+1+1
所以含 n n n 个关键字的 m m m 阶 B B B 数, l o g m ( n + 1 ) ≤ h ≤ l o g ⌈ m / 2 ⌉ n + 1 2 + 1 log_m(n + 1) \leq h \leq log_{\lceil m/2 \rceil} \frac{n + 1}{2} + 1 logm(n+1)≤h≤log⌈m/2⌉2n+1+1
核心要求:
构建 B B B 树时,如果在插入新的关键字后,导致原结点的关键字数超过上限,则从中间位置( ⌈ m / 2 ⌉ \lceil m/2 \rceil ⌈m/2⌉)将其中的关键字分为两部分,左部分包含的关键字放在原结点中,右部分包含的关键字放到新结点中,中间位置( ⌈ m / 2 ⌉ \lceil m/2 \rceil ⌈m/2⌉)的结点插入原结点的父结点;若此时导致其父结点的关键字个数也超过了上限,则继续进行这种分裂操作,直至这个过程传到根结点为止,进而导致 B B B 树高度增 1 1 1
每次插入新元素一定是插入到最底层 “终端结点”,用 “查找” 来确定插入位置
① 若被删除的关键字在终端结点,则直接删除该关键字即可(注意结点关键字个数是否低于下限 ⌈ m / 2 ⌉ − 1 \lceil m/2 \rceil - 1 ⌈m/2⌉−1
② 若被删除的关键字在非终端结点,则用直接前驱或直接后继来替代被删除的关键字
所以对非终端结点关键字的删除必然可以转化为对终端结点的删除操作
③ 兄弟够借。若被删除的关键字所在结点删除前的关键字个数低于下限,且与此结点右(或左)兄弟结点的关键字个数还很宽裕,则需要调整该结点、右(或左)兄弟结点及其双亲结点(父子换位法)
④ 兄弟不够借。若被删除的关键字所在结点删除前的关键字个数低于下限,且此时与该结点相邻的左、右兄弟结点的关键字个数均 = ⌈ m / 2 ⌉ − 1 = \lceil m/2 \rceil - 1 =⌈m/2⌉−1,则将关键字删除后与左(或右)兄弟结点及双亲结点中的关键字进行合并
在合并过程中,双亲结点中的关键字个数会减 1 1 1。若其双亲结点是根结点且关键字个数减少至 0 0 0(根结点关键字个数为 1 1 1 时,有 2 2 2 棵子树),则直接将根结点删除,合并后的新结点成为根;若双亲结点不是根结点,且关键字个数减少到 ⌈ m / 2 ⌉ − 2 \lceil m/2 \rceil - 2 ⌈m/2⌉−2,则又要与它自己的兄弟结点进行调整或合并操作,并重复上述操作,直至符合 B B B 树的要求为止
上图是一棵 4 4 4 阶 B + B+ B+ 树
一棵 m m m 阶 B + B+ B+ 树需满足下列条件:
m m m 阶 B B B 树:
m m m 阶 B + B+ B+ 树: