B树在数据结构中的搜索、插入、删除操作示例
什么是 B 树?
B 树是一种自平衡数据结构,它遵循一套特定的规则,以更快、更节省内存的方式来搜索、插入和删除数据。为了实现这一点,遵循以下规则来创建 B 树。
B 树是一种特殊类型的树形数据结构。1972 年,McCreight 首次介绍了这种方法,Bayer 将其命名为高度平衡的 m 路查找树。它有助于保持数据排序,并允许在更短的时间内进行插入、搜索和删除等各种操作。
B 树的规则
以下是创建 B_Tree 的重要规则
- 所有叶子节点都将在同一层创建。
- B 树由一个称为“阶”的次数决定(由外部执行者,如程序员指定),该次数也称为“阶”
m
开始。值
m
取决于主要位于数据所在的磁盘块大小。
- 节点的左子树将具有小于右子树的值。这意味着节点也从左到右按升序排序。
- 根节点及其子节点可以包含的最大子节点数量通过以下公式计算
m – 1
例如
m = 4 max keys: 4 – 1 = 3
- 除根节点外,每个节点必须包含最小键数
[m/2]-1
例如
m = 4 min keys: 4/2-1 = 1
- 节点可以拥有的最大子节点数量等于其阶,即
m
- 节点可以拥有的最小子节点数是阶的一半,即 m/2(取天花板值)。
- 节点中的所有键均按升序排序。
为什么使用 B 树
以下是使用 B 树的原因
- 减少磁盘读取次数
- B 树可以轻松优化以根据磁盘大小调整其大小(即子节点数量)
- 它是专门为处理大量数据而设计的一种技术。
- 它是数据库和文件系统的有用算法。
- 在读写大量数据时,这是一个不错的选择
B 树的历史
- 数据存储在磁盘块中,这些数据被加载到主内存(或 RAM)中时称为数据结构。
- 对于海量数据,在磁盘中搜索一个记录需要读取整个磁盘;由于高磁盘访问频率和数据大小,这会增加时间和主内存消耗。
- 为了克服这一点,会创建索引表,该表根据记录所在的块来保存记录的引用。这大大减少了时间和内存消耗。
- 由于我们拥有海量数据,因此可以创建多级索引表。
- 多级索引可以通过使用 B 树来设计,以自平衡的方式保持数据排序。
搜索操作
搜索操作是 B 树上最简单的操作。
应用以下算法
- 将要搜索的键(值)设为“k”。
- 从根开始搜索,并递归地向下遍历。
- 如果 k 小于根节点的值,则搜索左子树;如果 k 大于根节点的值,则搜索右子树。
- 如果节点包含找到的 k,则简单地返回该节点。
- 如果节点中未找到 k,则向下遍历到具有更大键的子节点。
- 如果树中未找到 k,则返回 NULL。
插入操作
由于 B 树是自平衡树,因此您不能强制将键插入任何节点。
应用以下算法
- 运行搜索操作并找到合适的插入位置。
- 将新键插入到正确的位置,但如果节点已包含最大数量的键
- 节点将连同新插入的键一起,从中间元素分裂。
- 中间元素将成为其他两个子节点的父节点。
- 节点必须按升序重新排列键。
提示 | 以下关于插入算法的说法不正确
由于节点已满,因此会分裂,然后插入新值 |
在上面的示例中
- 搜索节点中键的合适位置
- 将键插入目标节点,并检查规则
- 插入后,节点是否包含的键数大于或等于最小值 1?在这种情况下,是的。检查下一条规则。
- 插入后,节点是否包含的键数大于最大键数 3?在这种情况下,否。这意味着 B 树没有违反任何规则,插入已完成。
在上面的示例中
- 节点已达到最大键数
- 节点将分裂,中间键将成为其余两个节点的主节点。
- 如果键的数量是偶数,则中间节点将通过左偏或右偏选择。
在上面的示例中
- 节点包含的键少于最大键数
- 1 被插入到 3 旁边,但升序规则被违反
- 为了解决这个问题,键被排序
同样,13 和 2 可以轻松地插入到节点中,因为它们满足节点的最大键数规则。
在上面的示例中
- 节点包含的键等于最大键数。
- 将键插入目标节点,但违反了最大键数规则。
- 目标节点被分裂,通过左偏的中间键现在是新子节点的父节点。
- 新节点按升序排列。
同样,根据上述规则和案例,其余值可以轻松地插入 B 树。
删除操作
删除操作的规则比插入和搜索操作多。
应用以下算法
- 运行搜索操作并找到节点中的目标键
- 根据目标键的位置应用三个条件,如下节所述
如果目标键在叶节点中
- 目标在叶节点中,键数多于最小值。
- 删除此键不会违反 B 树的属性
- 目标在叶节点中,它有最小键数
- 删除此键会违反 B 树的属性
- 目标节点可以从其紧邻的左节点或紧邻的右节点(兄弟节点)借用键
- 如果兄弟节点具有的键数多于最小值,则它会说“是”
- 将从父节点借用键,最大值将转移到父节点,父节点的最大值将转移到目标节点,然后删除目标值
- 目标在叶节点中,但没有兄弟节点具有多于最小数量的键
- 搜索键
- 与兄弟节点和最小父节点合并
- 总键数现在将多于最小值
- 目标键将被父节点的最小值替换
如果目标键在内部节点中
- 选择中序前驱或中序后继
- 如果是中序前驱,则选择其左子树中的最大键
- 如果是中序后继,则选择其右子树中的最小键
- 只有当目标键的中序前驱的键数多于最小值时,它才能用中序前驱的最大值替换目标键
- 如果目标键的中序前驱的键数不大于最小值,则查找中序后继的最小值。
- 如果目标键的中序前驱和后继都少于最小值,则合并前驱和后继。
如果目标键在根节点中
- 替换为中序前驱子树中的最大元素
- 如果删除后,目标键的键数少于最小值,则目标节点将通过兄弟节点的父节点从其兄弟节点借用最大值。
- 父节点的最大值将被目标节点获取,但会与兄弟节点的最大值节点一起。
现在,让我们通过一个例子来理解删除操作。
上图显示了 B 树中删除操作的不同情况。此 B 树的阶为 5,这意味着任何节点可以拥有的最小子节点数为 3,而任何节点可以拥有的最大子节点数为 5。而任何节点可以拥有的最小和最大键数分别为 2 和 4。
在上面的示例中
- 目标节点包含要删除的目标键
- 目标节点包含的键数多于最小值
- 直接删除键
在上面的示例中
- 目标节点包含的键数等于最小值,因此不能直接删除,因为这会违反条件
现在,下图解释了如何删除此键
- 目标节点将从其紧邻的兄弟节点借用一个键,在这种情况下,中序前驱(左兄弟节点),因为它没有中序后继(右兄弟节点)
- 中序前驱的最大值将转移到父节点,父节点将最大值转移到目标节点(参见下图)
以下示例说明了如何删除需要从其中序后继借值的键。
- 目标节点将从其紧邻的兄弟节点借用一个键,在这种情况下,中序后继(右兄弟节点),因为它从中序前驱(左兄弟节点)借用的键数等于最小值。
- 中序后继的最小值将转移到父节点,父节点将最大值转移到目标节点。
在下面的示例中,目标节点没有可以将其键提供给目标节点的兄弟节点。因此,需要合并。
查看删除此类键的过程
- 将目标节点与任何一个紧邻的兄弟节点以及父键合并
- 从父节点中选择位于两个合并节点之间的键
- 从合并后的节点中删除目标键
删除操作伪代码
private int removeBiggestElement() { if (root has no child) remove and return the last element else { answer = subset[childCount-1].removeBiggestElement() if (subset[childCount-1].dataCount < MINIMUM) fixShort (childCount-1) return answer } }
输出:
B 树中删除了最大的元素。
摘要
- B 树是一种自平衡数据结构,用于更好地从磁盘搜索、插入和删除数据。
- B 树由指定的阶数进行调节
- B 树的键和节点按升序排列。
- B 树的搜索操作是最简单的,它总是从根开始,然后检查目标键是大于还是小于节点的值。
- B 树的插入操作相当详细,它首先找到目标键的合适插入位置,插入它,根据不同情况评估 B 树的有效性,然后相应地重构 B 树节点。
- B 树的删除操作首先搜索要删除的目标键,删除它,然后根据目标节点、兄弟节点和父节点的最小/最大键数等多个情况评估有效性。