代码实现
# search_mb_tree.py
from collections import deque
import math
m = 3
# m = 4
class MbtNode:
def __init__(self):
self.parent = None
self.key_num = 0
self.keys = deque()
self.sub_trees = deque()
# keys中keys[0]不会使用,keys[1]到keys[m-1]用于存储m-1个关键字,keys[m]用于插入第m个时结点会进行分裂,
# sub_trees[0]-sub_trees[m-1]用于存储m个子树,sub_trees[m]用于存储第m个时结点会进行分裂,
for i in range(m+1):
self.keys.append(None)
self.sub_trees.append(None)
self.info = None
class MbTree:
def __init__(self):
self.root = None
def search(mbt_node: MbtNode, key) -> int:
"""
在结点mbt_node中,寻找小于等于key的最大关键字序号
:param mbt_node: 在bmt_node中查找关键字key
:param key: 所查找关键字
:return: 返回key在mbt_node的keys中的所在位置或应插位置
"""
key_num = mbt_node.key_num
i = 1
while i <= key_num and mbt_node.keys[i] <= key:
i += 1
return i-1 # 返回key在mbt_node的keys中的所在位置或应插位置
def search_mb_tree(mb_tree: MbTree, key):
"""
在bm_tree的B树中查找关键字为key的结点,若查找成功,则将(所在结点、所在结点中的位置、True)返回;否则
将(key应插入的结点,插入结点的插入位置,False)返回
:param mb_tree: 查找的B树
:param key: 所查找的关键字
:return: 成功返回(所在结点、所在结点中的位置、True),失败返回(key应插入的结点,插入结点的插入位置,False)
"""
p = mb_tree.root
fp = None # p的双亲结点,当p为空时,fp就是key应插入的结点
i = 0 # 成功时i为key在所在结点中的位置,失败时为key应插入结点的插入位置
found = False # 表示是否找到key所在的结点
while p and not found:
# 退出循环时有两种情况
# (1) p为None时表示树中没有结点的keys中包含key
# (2) found为True时表示在树中找到key所在结点
i = search(p, key) # 查找key在p结点中的所在位置或应插位置
if p.keys[i] == key:
# 表示已找到key所在结点,需要退出循环
found = True
else:
# 表示为找到应到p.sub_trees[i]中去继续查找key
fp = p
p = p.sub_trees[i]
if found:
return p, i, True # 成功返回(所在结点、所在结点中的位置、True)
else:
return fp, i, False # 失败返回(key应插入的结点,插入结点的插入位置,False)
def insert(mbp: MbtNode, ipos: int, key, rp: MbtNode):
"""
在mbp结点的keys[ipos+1]处插入关键字key,sub_trees[ipos+1]处插入子结点rp
:param mbp:
:param ipos:
:param key:
:param rp:
:return: None
"""
mbp.keys.insert(ipos+1, key)
mbp.keys.pop()
mbp.sub_trees.insert(ipos+1, rp)
mbp.sub_trees.pop()
mbp.key_num += 1
def split(oldp: MbtNode):
"""
对oldp结点进行分裂
:param oldp: 所需分裂的旧结点
:return: # 返回根据旧结点右半部分所创建的新结点
"""
s = math.ceil(m/2) # 获取oldp的⌈m/2⌉的位置
n = m-s # 计算oldp的右半部分的key的个数
newp = MbtNode() # 创建新结点
newp.parent = oldp.parent # 新结点的双亲为旧结点的双亲
newp.key_num = n # 新结点的key的个数
if oldp.sub_trees[s]:
newp.sub_trees[0] = oldp.sub_trees[s] # 新结点的第0个子结点为oldp的第s个子结点
oldp.sub_trees[s] = None
newp.sub_trees[0].parent = newp # 将新结点的第一个子结点的双亲结点改为新结点
for i in range(1, n+1):
# 为新结点的keys和sub_trees赋值,新结点从下标为1处开始,旧结点从下标为s+1处开始
newp.keys[i] = oldp.keys[s+i]
oldp.keys[s + i] = None # 将旧结点的keys从s+1处变为None
if oldp.sub_trees[s+i]:
newp.sub_trees[i] = oldp.sub_trees[s+i]
oldp.sub_trees[s + i] = None # 将旧结点的sub_trees从s+1处变为None
newp.sub_trees[i].parent = newp # 将新结点的子结点的双亲结点改为新结点
oldp.keys[s] = None # 将旧结点的keys[s]变为None,keys[s]处的值会插入到旧结点和新结点的双亲结点中
oldp.key_num = s - 1 # 旧结点的key的个数变为⌈m/2⌉-1,而此时旧结点的子结点个数变为⌈m/2⌉
return newp # 返回新创建的结点
def ins_mbtree(mb_tree: MbTree, key, q: MbtNode, i):
"""
在m阶mb_tree树的q结点的i位置插入关键字key
算法思想:
如果mb_tree为None,则生成初始根(此时q=None, i=);否则q指向某个最下层非终端结点,key应插在该结点
中q.keys[i+1]处,插入后如果q.key_num > m-1,则进行分裂处理
:param mb_tree: 已创建好的B树
:param key: 插入的关键字
:param q: 关键字所插入的结点
:param i: 关键字在所插入结点中的位置
:return: 关键字已插入进去的B树
"""
if not mb_tree.root:
# 所创建好的的mb_tree是一棵空树
mb_tree.root = MbtNode()
mb_tree.root.key_num = 1
mb_tree.root.keys[1] = key
else:
# 所创建好的的mb_tree是一棵非空树
x = key # 将x插入到q.keys[i+1]处
ap = None # 将ap插入到q.sub_trees[i+1]处
finished = False # 表示关键字插入过程未完成
while q and not finished:
# 退出while循环有两种情况
# (1) q为None表示已经分裂到根,退出while后需创建新根;
# (2) finished为True表示分裂已完成,此时并未分裂到根,退出while后不需要创建新根。
insert(q, i, x, ap) # 在q结点的keys的下标为i+1位置插入x,sub_trees的下标为i+1位置插入ap
if q.key_num < m: # 判断关键字是否插入完成
# 表示关键字插入过程完成
finished = True # 插入完成时退出循环,此时并未分裂到根
else:
# 表示关键字插入过程完成,需要分裂结点q
s = math.ceil(m/2) # 获取q的⌈m/2⌉的位置
x = q.keys[s] # 获取q的keys的⌈m/2⌉处元素
q.keys[s] = None
ap = split(q) # 对q结点进行分裂,并获取根据q的右半部分所创建的新结点
q = q.parent # 将q结点重新设置为其双亲结点
if q: # 若双亲结点存在,则搜索关键字x在双亲结点中的应插入的下标值
i = search(q, x)
if not finished:
# 表示根结点要分裂,并产生新根
# 对创建新根结点并对其进行赋值操作
new_root = MbtNode()
new_root.key_num = 1
new_root.keys[1] = x
new_root.sub_trees[0] = mb_tree.root
new_root.sub_trees[1] = ap
# 为新根的子结点设置双亲
mb_tree.root.parent = new_root
ap.parent = new_root
# 将mb_tree根结点设置为新创建的新根结点
mb_tree.root = new_root
return mb_tree # 返回关键字已插入进去的B树
def create_mb_tree(mb_tree: MbTree, key_list: deque):
"""
创建一棵B树
:param mb_tree: 创建好的一棵空树
:param key_list: 关键字列表
:return: 创建好的一棵B树
"""
for key in key_list:
q, i, _ = search_mb_tree(mb_tree, key) # 寻找关键字插入结点q与在结点中得位置i
mb_tree = ins_mbtree(mb_tree, key, q, i) # 在mb_tree树的q结点的i位置插入关键字key
return mb_tree # 返回创建好的一棵B树
def is_bottom(mb_tree: MbTree, key):
p, i, success = search_mb_tree(mb_tree, key)
if success: # 判断mb_tree中是否存在包含关键字key的结点
# mb_tree中存在包含关键字key的结点
if p.sub_trees[0]: # 判断关键字key的所在结点是否是最底层结点
# 关键字key的所在结点不是最底层结点
is_bottom_ = False
else:
# 关键字key的所在结点是最底层结点
is_bottom_ = True
else:
# mb_tree中不存在包含关键字key的结点
is_bottom_ = p = i = None
return is_bottom_, p, i
def del_bottom(mb_tree: MbTree, key, p: MbtNode, i):
"""
删除最底层结点中的关键字
:param mb_tree: B树
:param key: 所需删除关键字
:param p: 所需删除关键字所在结点
:param i: 所需删除关键字在所在结点中的位置
:return: 删除关键字后的B树
"""
s = math.ceil(m/2)
if p.key_num > s - 1: # 判断p中的关键字数是否大于⌈m/2⌉-1 或 p结点是否是根结点
# p中的关键字数是否大于⌈m/2⌉-1 或 p结点是根结点
del p.keys[i] # 删除关键字key
p.keys.append(None) # 为保证 p.keys长度为m+1
p.key_num -= 1 # p结点的关键字字数减1
else:
# p中的关键字数不大于⌈m/2⌉-1
p_parent = p.parent # 获取p的双亲结点
j = search(p_parent, key) # 获取key所在结点在双亲结点中的应插位置j,j也是key所在结点p在p_parent.sub_trees中的下标,
p_left_sibling = p_parent.sub_trees[j-1]
p_right_sibling = p_parent.sub_trees[j+1]
if j > 0 and p_left_sibling.key_num > s - 1:
# p的左兄弟结点关键字个数大于⌈m/2⌉-1,当j==0时,p没有左兄弟,不能使用该部分代码
del p.keys[i] # 删除关键字key
p.keys.insert(1, p_parent.keys[j]) # 将p_parent.keys[j]插入p.keys的开始位置
p_parent.keys[j] = p_left_sibling.keys[p_left_sibling.key_num] # 将key所在结点p的左兄弟结点的最后一个关键字赋值给key所在结点p的双亲结点的第j个关键字处,保持中序
del p_left_sibling.keys[p_left_sibling.key_num] # 删除p_left_sibling.keys[p_left_sibling.key_num]的最后一个关键字删除
p_left_sibling.keys.append(None) # 保证p_left_sibling.keys的长度为m+1
p_left_sibling.key_num -= 1 # 将key所在结点p的左兄弟结点的关键字个数减1
elif j < p_parent.key_num and p_right_sibling.key_num > s - 1:
# p的右兄弟结点关键字个数大于⌈m/2⌉-1,当j==p_parent.key_num时p没有右兄弟,不能使用该部分代码
del p.keys[i] # 删除关键字key
p.keys.insert(p.key_num, p_parent.keys[j]) # 将key所在结点p的双亲结点的第j个关键字添加到key所在关键字结点的最后,保持中序
p_parent.keys[j] = p_right_sibling.keys[1] # 将key所在结点p的右兄弟结点的第一个关键字赋值给key所在结点p的双亲结点的第j个关键字处,保持中序
del p_right_sibling.keys[1] # 删除_right_sibling.keys的第一个关键字删除
p_right_sibling.keys.append(None) # 保证p_right_sibling.keys的长度为m+1
p_right_sibling.key_num -= 1 # 将key所在结点p的右兄弟结点的关键字个数减1
else:
# p的左右兄弟结点关键字个数都不大于⌈m/2⌉-1
del p.keys[i] # 删除p中的keys中的第i个关键字
p.keys.append(None) # 保持p.keys的长度为m+1
p.key_num -= 1 # p的关键字个数减1
while p.parent:
# 表示当前结点p已经为根结点退出循环
p_parent = p.parent # 获取p结点的双亲结点
j = search(p_parent, key) # 在p的双亲结点中搜索应插位置
p_left_sibling = p_parent.sub_trees[j - 1] # 获取当前结点的左兄弟结点
p_right_sibling = p_parent.sub_trees[j + 1] # 获取当前结点的右兄弟结点
if j > 0:
# 当j==0时,p没有左兄弟,不能使用该部分代码
p.keys.insert(1, p_parent.keys[j]) # 将p_parent.keys[j]插入p的关键字的第一个位置
p.keys.pop() # 为保证 p.keys长度为m+1
p.key_num += 1 # p的关键字个数加1
for k in range(1, p.key_num+1): # 将p中的关键字及子树合并到其左兄弟中,放到左兄弟的最后
p_left_sibling.keys[p_left_sibling.key_num+k] = p.keys[k] # 将p.keys值依次插入p_left_sibling.keys尾部
p_left_sibling.sub_trees[p_left_sibling.key_num+k] = p.sub_trees[k] # psub_trees的第0个已为空,从第p.key_num个开始
p_left_sibling.key_num += p.key_num # p的左兄弟关键字个数需再加上p的关键字个数
del p_parent.keys[j] # 删除p的双亲结点中关键字p_parent.keys[j]
p_parent.keys.append(None) # 为保证 p_parent.keys长度为m+1
p_parent.key_num -= 1 # p的双亲结点个数减1
del p_parent.sub_trees[j] # 从p的双亲结点中删除p结点
p_parent.sub_trees.append(None) # 为保证 p_parent.sub_trees 长度为m+1
else:
# 当j==p_parent.key_num时p没有右兄弟,不能使用该部分代码
p.keys.insert(p.key_num+1, p_parent.keys[j+1]) # 将p_parent.keys[j+1]插入p的最后一个位置
p.keys.pop() # 为保证 p.keys长度为m+1
p.key_num += 1 # p的关键字个数加1
for k in range(p.key_num, 0, -1): # # 将p中的关键字及子树合并到其右兄弟中,放到右兄弟的最开始
p_right_sibling.keys.insert(1, p.keys[k]) # 将p.keys值依次插入p_right_sibling.keys的第1个位置
p_right_sibling.keys.pop() # 为保证 p_right_sibling.keys长度为m+1
p_right_sibling.sub_trees.insert(0, p.sub_trees[k-1]) # p.sub_trees第p.key_num个已删除从第p.key_num-1个开始
p_right_sibling.sub_trees.pop() # 为保证 p_right_sibling.sub_trees长度为m+1
del p_parent.keys[j+1] # 删除p的双亲结点中关键字p_parent.keys[j+1]
p_parent.keys.append(None) # 为保证 p_parent.keys长度为m+1
p_parent.key_num -= 1 # p的双亲结点个数减1
del p_parent.sub_trees[j] # 从p的双亲结点中删除p结点
p_parent.sub_trees.append(None) # 为保证 p_parent.sub_trees 长度为m+1
if p_parent.key_num >= s - 1:
# 当前结点p的关键字数大于等于⌈m / 2⌉-1,结点合并已完成,退出循环
break
p = p.parent
if not p.parent: # 判断p结点是否是根结点
# 表示p是根结点
if p_left_sibling:
# p_left_sibling与p_right_sibling中必有一个不为空作为根结点
mb_tree.root = p_left_sibling
else:
mb_tree.root = p_right_sibling
return mb_tree # 删除关键字后的B树
def del_non_bottom(mb_tree: MbTree, p: MbtNode, i):
"""
删除非最下层结点中的关键字
算法思想:
(1) 找到所删关键字结点的右子结点的最左下端的结点的第一个关键字替换掉所删关键字,然后转化为删除右子
结点的最左下端的结点的第一个关键字。
(2) 找到所删关键字结点的左子结点的最右下端的结点的最后一个关键字替换掉所删关键字,然后转化为删除左
子结点的最右下端的结点的最后一个关键字替换掉所删关键字。
以上两种方法都可,此处使用第一种方法,总之,删除非最下层结点中的关键字可转化为删除最下层结点中的关
键字。
:param mb_tree: B树
:param p: 所删关键字所在结点
:param i: 所删关键字在结点的keys的下标
:return: 删除关键字后的B树
"""
q = p.sub_trees[i]
while q.sub_trees[0]:
q = q.sub_trees[0]
p.keys[i] = q.keys[1]
mb_tree = del_bottom(mb_tree, q.keys[1], q, 1)
return mb_tree
def del_mb_tree(mb_tree: MbTree, key):
"""
删除B树中关键字key
:param mb_tree: B树
:param key: 所删关键字
:return: 删除关键字后的B树
"""
is_bottom_, p, i = is_bottom(mb_tree, key)
if is_bottom_ is None: # 判断mb_tree中是否存在包含关键字key的结点
return -1 # 表示mb_tree中不存在包含关键字key的结点
if is_bottom_: # 判断关键字key的所在结点是否是最底层结点
# 关键字key的所在结点是最底层结点
mb_tree = del_bottom(mb_tree, key, p, i)
else:
# 关键字key的所在结点不是最底层结点
mb_tree = del_non_bottom(mb_tree, p, i)
return mb_tree # 返回删除关键字key以后的mb_tree
# test_search_mb_tree
from collections import deque
from search_mb_tree import MbTree, create_mb_tree, search_mb_tree, del_mb_tree
# 测试create_mb_tree和search_mb_tree
# mb_tree = MbTree()
# mb_tree = create_mb_tree(mb_tree, deque([37, 70, 12, 45, 90, 3, 24, 61]))
# print("37", mb_tree.root.key_num, mb_tree.root.keys)
# print("12", mb_tree.root.sub_trees[0].key_num, mb_tree.root.sub_trees[0].keys)
# print("70", mb_tree.root.sub_trees[1].key_num, mb_tree.root.sub_trees[1].keys)
# print("3", mb_tree.root.sub_trees[0].sub_trees[0].key_num, mb_tree.root.sub_trees[0].sub_trees[0].keys)
# print("24", mb_tree.root.sub_trees[0].sub_trees[1].key_num, mb_tree.root.sub_trees[0].sub_trees[1].keys)
# print("45, 61", mb_tree.root.sub_trees[1].sub_trees[0].key_num, mb_tree.root.sub_trees[1].sub_trees[0].keys)
# print("90", mb_tree.root.sub_trees[1].sub_trees[1].key_num, mb_tree.root.sub_trees[1].sub_trees[1].keys)
# q, i, success = search_mb_tree(mb_tree, 45)
# print(q.keys, i, success)
# 测试删除非终端结点
# print("="*30)
mb_tree = MbTree()
mb_tree = create_mb_tree(mb_tree, deque([37, 70, 12, 45, 90, 3, 24, 61]))
# print("37", mb_tree.root.key_num, mb_tree.root.keys)
# print("12", mb_tree.root.sub_trees[0].key_num, mb_tree.root.sub_trees[0].keys)
# print("70", mb_tree.root.sub_trees[1].key_num, mb_tree.root.sub_trees[1].keys)
# print("3", mb_tree.root.sub_trees[0].sub_trees[0].key_num, mb_tree.root.sub_trees[0].sub_trees[0].keys)
# print("24", mb_tree.root.sub_trees[0].sub_trees[1].key_num, mb_tree.root.sub_trees[0].sub_trees[1].keys)
# print("45, 61", mb_tree.root.sub_trees[1].sub_trees[0].key_num, mb_tree.root.sub_trees[1].sub_trees[0].keys)
# print("90", mb_tree.root.sub_trees[1].sub_trees[1].key_num, mb_tree.root.sub_trees[1].sub_trees[1].keys)
print("del 61".center(30, '='))
mb_tree = del_mb_tree(mb_tree, 61)
# print("37", mb_tree.root.key_num, mb_tree.root.keys)
# print("12", mb_tree.root.sub_trees[0].key_num, mb_tree.root.sub_trees[0].keys)
# print("70", mb_tree.root.sub_trees[1].key_num, mb_tree.root.sub_trees[1].keys)
# print("3", mb_tree.root.sub_trees[0].sub_trees[0].key_num, mb_tree.root.sub_trees[0].sub_trees[0].keys)
# print("24", mb_tree.root.sub_trees[0].sub_trees[1].key_num, mb_tree.root.sub_trees[0].sub_trees[1].keys)
# print("45", mb_tree.root.sub_trees[1].sub_trees[0].key_num, mb_tree.root.sub_trees[1].sub_trees[0].keys)
# print("90", mb_tree.root.sub_trees[1].sub_trees[1].key_num, mb_tree.root.sub_trees[1].sub_trees[1].keys)
print('del 24'.center(30, '+'))
mb_tree = del_mb_tree(mb_tree, 24)
print("37, 70", mb_tree.root.key_num, mb_tree.root.keys)
print("3, 12", mb_tree.root.sub_trees[0].key_num, mb_tree.root.sub_trees[0].keys)
print("45", mb_tree.root.sub_trees[1].key_num, mb_tree.root.sub_trees[1].keys)
print("90", mb_tree.root.sub_trees[2].key_num, mb_tree.root.sub_trees[2].keys)
[引用《数据结构–用C语言描述》耿国华版]