1. m路查找树
与二叉排序树类似,可以定义一种“m叉排序树”,通常称为m路查找树。
一棵m路查找树,或者是一棵空树,或者是满足如下性质的树:
① 结点最多有m棵子树,m-1个关键字,其结点结构如下图所示:
其中,n为关键字的个数,Pi(0<=i<=n)为指向子树根结点的指针,Ki(1<=i<=n)为关键字。
② Ki<Ki+1,1<=i<=n-1
③ 子树Pi中的所有关键字均大于Ki且小于Ki+1,1<=i<=n-1
④ 子树P0中的关键字均小于K1,而子树Pn中的所有关键字均大于Kn
⑤ 子树Pi也是m路查找树,0<=i<=n
2. B树
一棵B树是一棵平衡的m路查找树,它或者是空树,或者是满足如下性质的树:
① 树中每个结点最多有m棵子树。
② 根结点至少有两棵子树。
③ 除根结点之外的所有非叶结点至少有⌈m/2⌉棵子树。
④ 所有的叶结点出现在同一层上,并且不含信息,通常称为失败结点。失败结点为虚结点,在B树中并不存在,指向它们的指针为空指针。引入失败结点是为了便于分析B树的查找性能。
B树结点结构:
typedef int KeyType;
/*B树结点存储结构*/
typedef struct Mbtnode {
struct Mbtnode* parent; //指向双亲结点的指针
int keynum; //结点关键字个数
KeyType key[m + 1]; //存放结点关键字
struct Mbtnode* ptr[m + 1]; //指向子树的指针
}Mbtnode, * Mbtree;
问题1:具有n个非叶结点的m阶B树,至少含有多少个关键字?
分析:根据m阶B树的定义,根结点至少有两棵子树和一个关键字,除根结点之外的所有非叶结点,每个结点至少有⌈m/2⌉棵子树和⌈m/2⌉-1个关键字。综上,具有n个非叶结点的m阶B树至少含有 1+(⌈m/2⌉-1)*(n-1) 个关键字。
问题2:已知一棵3阶B树中含有2047个关键字,那么包含叶子结点(失败结点)层,该树的最大深度是多少?
分析:根据m阶B树的定义,3阶B树的根结点至少有两棵子树,除根结点之外的所有非叶结点,每个结点至少有⌈m/2⌉=2棵子树,所以该3阶B树的所有非叶结点对应一棵满二叉树,每一个结点中有一个关键字,共有2047个关键字,所以共有2047个结点,其深度为⌊log2 2047⌋+1=11,加上失败结点,最大深度为12。
3. B树查找
对于上图所示的4阶B树,查找过程如下:
① 查找58:由根结点指针mbt找到根结点A → 58>37,找到结点C → 40<58<85,找到结点G → 在G中找到58
② 查找32:由根结点指针mbt找到根结点A → 32<37,找到结点B → 32>25,找到结点E → 30<32<35,找到失败结点f → 查找失败
/*寻找结点中小于等于key的最大关键字序号*/
int Search(Mbtree mbt, KeyType key) {
int i = 1;
while (i <= mbt->keynum && mbt->key[i] <= key)
i++;
return (i - 1); //返回小于等于key的最大关键字序号,为0时表示应到最左分支找,越界时表示应到最右分支找
}
/*查找关键字为k的结点*/
int srch_mbtree(Mbtree mbt, KeyType k) {
Mbtnode* p = mbt;
int found = 0, i;
while (p != NULL && !found) {
i = Search(p, k);
if (i > 0 && p->key[i] == k) //查找成功,found置1
found = 1;
else //k不在当前结点,查找下一结点
p = p->ptr[i];
}
if (found) {
return p->key[i];
}
else
return NULL;
}
4. B树插入
5. B树删除
5.1 在最下层结点中删除一个关键字
下图为一棵4阶B树:
① 当最下层结点中的关键字数目大于⌈m/2⌉-1时,可直接删除。
如删除11和53时,可直接删除,结果如下:
② 当最下层待删关键字所在结点中的关键字数目为最低要求⌈m/2⌉-1时,如果其左(右)兄弟中关键字数目大于⌈m/2⌉-1,则可采用“父子换位法”。
如删除39时,为保持其“中序有序”,可将父结点中43下移至39处,而将右兄弟中最左边的47上移至原43处,结果如下:
③ 当最下层待删关键字所在结点及其左(右)兄弟中的关键字数目均为最低要求⌈m/2⌉-1时,需要进行合并处理,合并过程与插入时的分裂过程“互逆”,合并一次,分支数少一,可能出现连锁合并,当合并到根时,各分支深度同时减1。
如删除64时,为保持各分支等长,将删除64后的剩余信息及78合并入右兄弟,结果如下:
④ 删除27时,首先将剩余信息与父结点中的18并入左兄弟,并释放空结点。
此时父结点也需要合并,将父结点中的剩余信息(指针p1)与祖父结点中的35并入47左端。
至此,祖父结点仍需合并,但由于待合并结点的父指针为NULL,故停止合并,直接将根指针BT置为指针p2的值,释放空结点。
5.2 在非最下层结点中删除一个关键字
在下图所示4阶B树删除43、35过程:
① 删除43:在保持“中序有序”的前提下,可将43“右子树”中的最小值47顶替43,然后将原47删除。
② 删除35:用35“左子树”的“右下端”元素27代替35,然后再删除原27。
参考:耿国华《数据结构——用C语言描述(第二版)》
更多数据结构内容关注我的《数据结构》专栏:https://blog.csdn.net/weixin_51450101/category_11514538.html?spm=1001.2014.3001.5482