B树的分裂与合并操纵详解

打印 上一主题 下一主题

主题 940|帖子 940|积分 2820

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
B树的分裂与合并操纵详解

弁言

B树是一种自平衡的树数据布局,广泛应用于数据库和文件体系中,以维持有序数据并允许高效的插入、删除和搜刮操纵。作为一种多路搜刮树,B树的每个节点可以有多个子节点和多个键值。B树的关键特性在于其通太过裂和合并操纵来保持平衡。本文将具体介绍B树的分裂与合并操纵,并提供具体的源码示例。
B树基础知识

在讨论B树的分裂与合并操纵之前,先扼要回顾一下B树的根本概念和性质。
B树的定义

B树是一种平衡的多路搜刮树,满足以下性质:

  • 每个节点最多有m个子节点(称为m阶B树)。
  • 除根节点外,每个节点至少有⌈m/2⌉个子节点。
  • 根节点至少有两个子节点(假如非叶子节点)。
  • 每个节点包罗k-1个键值和k个子节点指针,其中⌈m/2⌉ ≤ k ≤ m。
  • 全部叶子节点都位于同一层。
B树的布局

B树节点的布局如下所示:
  1. Node:
  2.     keys: [k1, k2, ..., kn]
  3.     children: [c0, c1, ..., cn]
  4.     n: 当前节点中的键值数量
  5.     leaf: 是否为叶子节点
复制代码
B树的分裂操纵

分裂操纵是B树在插入过程中用于保持平衡的关键操纵。当一个节点中的键值数量到达最大值m-1时,该节点必要分裂为两个节点,并将中间键提拔到父节点中。
分裂操纵的步骤


  • 创建一个新的节点,作为分裂后的一半。
  • 将中间键及其左侧的键值和子节点保存在原节点中。
  • 将中间键右侧的键值和子节点移动到新节点中。
  • 将中间键提拔到父节点中。假如父节点也满了,递归实行分裂操纵。
分裂操纵的示例

假设我们有一个B树节点,包罗以下键值:
  1. Node:
  2.     keys: [10, 20, 30, 40, 50]
  3.     children: [c0, c1, c2, c3, c4, c5]
  4.     n: 5
  5.     leaf: False
复制代码
在插入新键值60时,节点已满,必要进行分裂操纵:

  • 创建一个新节点:
  1. New Node:
  2.     keys: []
  3.     children: []
  4.     n: 0
  5.     leaf: False
复制代码

  • 将中间键及其左侧键值保存在原节点中:
  1. Original Node (after split):
  2.     keys: [10, 20]
  3.     children: [c0, c1, c2]
  4.     n: 2
  5.     leaf: False
复制代码

  • 将中间键右侧的键值和子节点移动到新节点中:
  1. New Node (after split):
  2.     keys: [40, 50, 60]
  3.     children: [c3, c4, c5, c6]
  4.     n: 3
  5.     leaf: False
复制代码

  • 将中间键30提拔到父节点中:
  1. Parent Node (after split):
  2.     keys: [30]
  3.     children: [Original Node, New Node]
  4.     n: 1
  5.     leaf: False
复制代码
B树的合并操纵

合并操纵是B树在删除过程中用于保持平衡的关键操纵。当一个节点中的键值数量低于最小值⌈m/2⌉-1时,该节点必要与其相邻的兄弟节点合并,大概从父节点借一个键值。
合并操纵的步骤


  • 查抄节点的左兄弟和右兄弟,选择一个键值数量大于最小值的兄弟节点。
  • 将父节点中的键值下移到当前节点中。
  • 将兄弟节点中的键值上移到父节点中。
  • 假如兄弟节点的键值数量也低于最小值,合并当前节点和兄弟节点,并递归实行合并操纵。
合并操纵的示例

假设我们有一个B树节点,包罗以下键值:
  1. Parent Node:
  2.     keys: [20, 40]
  3.     children: [Node1, Node2, Node3]
  4.     n: 2
  5.     leaf: False
复制代码
删除节点Node2中的键值30后,节点中的键值数量低于最小值,必要进行合并操纵:

  • 查抄左兄弟Node1和右兄弟Node3,选择一个键值数量大于最小值的兄弟节点。假设选择右兄弟Node3:
  1. Node3:
  2.     keys: [50, 60]
  3.     children: [c5, c6, c7]
  4.     n: 2
  5.     leaf: False
复制代码

  • 将父节点中的键值40下移到当前节点Node2中:
  1. Node2 (after borrow):
  2.     keys: [40]
  3.     children: [c3, c4]
  4.     n: 1
  5.     leaf: False
复制代码

  • 将右兄弟Node3中的键值50上移到父节点中:
  1. Parent Node (after borrow):
  2.     keys: [20, 50]
  3.     children: [Node1, Node2, Node3]
  4.     n: 2
  5.     leaf: False
复制代码
假如右兄弟Node3的键值数量也低于最小值,则必要进行合并操纵:

  • 将当前节点Node2与右兄弟Node3合并:
  1. Node2 (after merge):
  2.     keys: [40, 50, 60]
  3.     children: [c3, c4, c5, c6, c7]
  4.     n: 3
  5.     leaf: False
复制代码

  • 将父节点中的键值50下移到当前节点Node2中,删除右兄弟Node3:
  1. Parent Node (after merge):
  2.     keys: [20]
  3.     children: [Node1, Node2]
  4.     n: 1
  5.     leaf: False
复制代码
源码示例

以下是B树的分裂与合并操纵的完整源码示例,使用Python语言实现。
  1. class BTreeNode:
  2.     def __init__(self, t, leaf=False):
  3.         self.t = t  # B树的最小度数
  4.         self.keys = []
  5.         self.children = []
  6.         self.leaf = leaf
  7. class BTree:
  8.     def __init__(self, t):
  9.         self.root = BTreeNode(t, True)
  10.         self.t = t
  11.     def insert(self, k):
  12.         root = self.root
  13.         if len(root.keys) == 2 * self.t - 1:
  14.             s = BTreeNode(self.t, False)
  15.             s.children.append(self.root)
  16.             self.split_child(s, 0)
  17.             self.root = s
  18.             self.insert_non_full(s, k)
  19.         else:
  20.             self.insert_non_full(root, k)
  21.     def insert_non_full(self, x, k):
  22.         i = len(x.keys) - 1
  23.         if x.leaf:
  24.             x.keys.append(None)
  25.             while i >= 0 and k < x.keys[i]:
  26.                 x.keys[i + 1] = x.keys[i]
  27.                 i -= 1
  28.             x.keys[i + 1] = k
  29.         else:
  30.             while i >= 0 and k < x.keys[i]:
  31.                 i -= 1
  32.             i += 1
  33.             if len(x.children[i].keys) == 2 * self.t - 1:
  34.                 self.split_child(x, i)
  35.                 if k > x.keys[i]:
  36.                     i += 1
  37.             self.insert_non_full(x.children[i], k)
  38.     def split_child(self, x, i):
  39.         t = self.t
  40.         y = x.children[i]
  41.         z = BTreeNode(t, y.leaf)
  42.         x.children.insert(i + 1, z)
  43.         x.keys.insert(i, y.keys[t - 1])
  44.         z.keys = y.keys[t:]
  45.         y.keys = y.keys[:t - 1]
  46.         if not y.leaf:
  47.             z.children = y.children[t:]
  48.             y.children = y.children[:t]
  49.     def delete(self, k):
  50.         self._delete(self.root, k)
  51.         if len(self.root.keys) == 0 and not self.root.leaf:
  52.             self.root = self.root.children[0]
  53.     def _delete(self, x, k):
  54.         t = self.t
  55.         if k in x.keys:
  56.             if x.leaf:
  57.                 x.keys.remove(k)
  58.             else:
  59.                 idx = x.keys.index(k)
  60.                 if len(x.children[idx].keys) >= t:
  61.                     predecessor = self.get_predecessor(x, idx)
  62.                     x.keys[idx] = predecessor
  63.                     self._delete(x.children[idx], predecessor)
  64.                 elif len(x.children[idx + 1].keys) >= t:
  65.                     successor = self.get_successor(x, idx)
  66.                     x.keys[idx] = successor
  67.                     self._delete(x.children[idx + 1], successor)
  68.                 else:
  69.                     self.merge(x, idx)
  70.                     self._delete(x.children[idx], k)
  71.         else:
  72.             if x.leaf:
  73.                 return
  74.             idx = self.find_key(x, k)
  75.             if len(x.children[idx].keys) < t:
  76.                 if idx > 0 and len(x.children[idx - 1].keys) >= t:
  77.                     self.borrow_from_prev(x, idx)
  78.                 elif idx < len(x.children) - 1 and len(x.children[idx + 1].keys) >= t:
  79.                     self.borrow_from_next(x, idx)
  80.                 else:
  81.                     if idx != len(x.children) - 1:
  82.                         self.merge(x, idx)
  83.                     else:
  84.                         self.merge(x, idx - 1)
  85.             self._delete(x.children[idx], k)
  86.     def get_predecessor(self, x, idx):
  87.         current = x.children[idx]
  88.         while not current.leaf:
  89.             current = current.children[-1]
  90.         return current.keys[-1]
  91.     def get_successor(self, x, idx):
  92.         current = x.children[idx + 1]
  93.         while not current.leaf:
  94.             current = current.children[0]
  95.         return current.keys[0]
  96.     def merge(self, x, idx):
  97.         t = self.t
  98.         child = x.children[idx]
  99.         sibling = x.children[idx + 1]
  100.         child.keys.append(x.keys[idx])
  101.         child.keys.extend(sibling.keys)
  102.         if not child.leaf:
  103.             child.children.extend(sibling.children)
  104.         x.keys.pop(idx)
  105.         x.children.pop(idx + 1)
  106.     def borrow_from_prev(self, x, idx):
  107.         child = x.children[idx]
  108.         sibling = x.children[idx - 1]
  109.         child.keys.insert(0, x.keys[idx - 1])
  110.         x.keys[idx - 1] = sibling.keys.pop()
  111.         if not child.leaf:
  112.             child.children.insert(0, sibling.children.pop())
  113.     def borrow_from_next(self, x, idx):
  114.         child = x.children[idx]
  115.         sibling = x.children[idx + 1]
  116.         child.keys.append(x.keys[idx])
  117.         x.keys[idx] = sibling.keys.pop(0)
  118.         if not child.leaf:
  119.             child.children.append(sibling.children.pop(0))
  120.     def find_key(self, x, k):
  121.         idx = 0
  122.         while idx < len(x.keys) and x.keys[idx] < k:
  123.             idx += 1
  124.         return idx
  125.     def traverse(self, x=None, depth=0):
  126.         if x is None:
  127.             x = self.root
  128.         print(" " * depth, x.keys)
  129.         if not x.leaf:
  130.             for child in x.children:
  131.                 self.traverse(child, depth + 4)
  132.     def search(self, k, x=None):
  133.         if x is None:
  134.             x = self.root
  135.         i = 0
  136.         while i < len(x.keys) and k > x.keys[i]:
  137.             i += 1
  138.         if i < len(x.keys) and k == x.keys[i]:
  139.             return True
  140.         if x.leaf:
  141.             return False
  142.         return self.search(k, x.children[i])
  143. # 测试B树
  144. btree = BTree(3)
  145. for i in range(10):
  146.     btree.insert(i)
  147. print("Initial tree:")
  148. btree.traverse()
  149. btree.delete(3)
  150. print("\nTree after deleting 3:")
  151. btree.traverse()
  152. btree.delete(6)
  153. print("\nTree after deleting 6:")
  154. btree.traverse()
复制代码
结论

通过本文的具体介绍,我们理解了B树的分裂与合并操纵的原理和实现方式。分裂操纵在插入过程中用于保持B树的平衡,而合并操纵在删除过程中用于保持B树的平衡。这些操纵确保了B树能够高效地实行插入、删除和搜刮操纵。通过源码示例,我们展示了如何在现实编程中实现这些操纵,并测试了它们的正确性。希望本文对读者深入理解B树的工作机制有所资助,并能够在现实项目中应用B树。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

小小小幸运

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表