数据结构第11节: B树

B树是一种自平衡的树数据结构,它能够保持数据排序,并且在插入、删除和查找操作中具有对数时间复杂度。B树广泛应用于文件系统、数据库和索引中,因为它们可以有效地处理大量数据。

B树的特点:

  1. 所有叶子节点都位于同一层。
  2. 每个节点可以有多个子节点(至少两个,最多为某个特定的最大值)。
  3. 节点包含一个关键字列表,这些关键字将子树分为不同的范围。
  4. 节点的关键字数量总是小于或等于其子节点的数量减一。
  5. 每个节点的关键字都是按照升序排列的。

下面是一个Python实现的B树:

class BTreeNode:def __init__(self, t, leaf=False):self.t = tself.leaf = leafself.keys = []self.children = []class BTree:def __init__(self, t):self.root = BTreeNode(t, True)self.t = tdef insert(self, k):root = self.rootif len(root.keys) == (2 * self.t) - 1:s = BTreeNode(self.t, False)self.root = ss.children.insert(0, root)self.split_child(s, 0)self.insert_non_full(s, k)else:self.insert_non_full(root, k)def insert_non_full(self, x, k):i = len(x.keys) - 1if x.leaf:x.keys.append((None, None))while i >= 0 and k[0] < x.keys[i][0]:x.keys[i + 1] = x.keys[i]i -= 1x.keys[i + 1] = kelse:while i >= 0 and k[0] < x.keys[i][0]:i -= 1i += 1if len(x.children[i].keys) == (2 * self.t) - 1:self.split_child(x, i)if k[0] > x.keys[i][0]:i += 1self.insert_non_full(x.children[i], k)def split_child(self, x, i):t = self.ty = x.children[i]z = BTreeNode(t, y.leaf)x.children.insert(i + 1, z)x.keys.insert(i, y.keys[t - 1])z.keys = y.keys[t: (2 * t) - 1]y.keys = y.keys[0: t - 1]if not y.leaf:z.children = y.children[t: 2 * t]y.children = y.children[0: t - 1]def delete(self, k):# deletion code here...passdef search(self, k):# search code here...pass

在这个例子中,我们定义了两个类:BTreeNodeBTreeBTreeNode 类表示B树中的每个节点,而 BTree 类表示整个B树。我们实现了插入和分裂操作,但删除和搜索操作尚未实现。

案例分析:

假设我们要创建一个t=3的B树,并插入以下关键字:(10, ‘a’), (20, ‘b’), (30, ‘c’), (40, ‘d’), (50, ‘e’)。

首先,我们创建一个空的B树,然后插入第一个关键字 (10, ‘a’)。由于树是空的,这个关键字将成为根节点的唯一关键字。

接下来,我们插入第二个关键字 (20, ‘b’)。因为根节点还没有达到最大容量(2 * t - 1 = 5),我们可以直接将新关键字插入到适当的位置。

然后,我们插入第三个关键字 (30, ‘c’)。同样地,由于根节点还没有达到最大容量,我们可以直接将新关键字插入到适当的位置。

接下来,我们插入第四个关键字 (40, ‘d’)。此时,根节点已达到最大容量(5),我们需要创建一个新的父节点,并将根节点分裂成两个子节点。我们将根节点的中间关键字 (20, ‘b’) 移动到新的父节点上,然后将剩下的关键字分别放入左右子节点。

最后,我们插入第五个关键字 (50, ‘e’)。由于根节点的右子节点还没有达到最大容量,我们可以直接将新关键字插入到适当的位置。

最终的B树如下所示:

          (20, 'b')/     \(10, 'a')   (30, 'c')\(40, 'd')\(50, 'e')

在上一个案例中,我们构建了一个t=3的B树并插入了一些关键字。现在,让我们继续分析如何从这个B树中删除一个关键字,例如 (20, ‘b’)。

在B树中,删除操作可能涉及到以下步骤:

  1. 查找要删除的关键字。
  2. 如果找到的关键字在叶节点中,则直接删除。
  3. 如果找到的关键字在非叶节点中,则需要找到后继或前驱节点,用它来替换要删除的关键字,然后递归地删除该后继或前驱节点。
  4. 在删除节点后,如果该节点的关键字数量低于最小限制(t-1),则可能需要与相邻的兄弟节点合并或重新分配关键字以保持B树的平衡。

现在,我们来删除 (20, ‘b’)。

  1. 首先,我们在B树中查找关键字 (20, ‘b’)。在本例中,(20, ‘b’) 是根节点的关键字。

  2. 因为 (20, ‘b’) 在非叶节点中,我们需要找到它的后继节点。后继节点是当前节点右侧子树中的最小关键字。在本例中,后继节点是 (30, ‘c’)。

  3. 我们用 (30, ‘c’) 替换 (20, ‘b’)。现在,B树如下所示:

          (30, 'c')/     \(10, 'a')   (40, 'd')\(50, 'e')
  1. 现在,我们需要递归地删除 (30, ‘c’)。但是,(30, ‘c’) 已经被用作替换,因此不需要进一步删除。

  2. 下一步是检查是否有节点的关键字数量低于最小限制(t-1)。在这种情况下,根节点只有一个关键字,这满足了最小限制。因此,我们不需要进行任何额外的操作。

最终的B树如下所示:

          (30, 'c')/     \(10, 'a')   (40, 'd')\(50, 'e')

如果我们继续向此B树中添加或删除关键字,B树将自动进行调整,以保持平衡和排序特性。这种自平衡和排序特性使得B树非常适合用于大型数据集和外部存储,如文件系统、数据库和索引。

在之前的案例分析中,我们讨论了如何在B树中插入和删除关键字。现在,让我们通过实际的源代码实现来进一步了解B树的删除操作。

在我们之前给出的B树代码示例中,delete 方法尚未实现。为了实现删除操作,我们需要添加以下代码:

def delete(self, k):root = self.rootself.delete_entry(root, k)def delete_entry(self, x, k):i = 0while i < len(x.keys) and k[0] > x.keys[i][0]:i += 1if x.leaf:if i < len(x.keys) and x.keys[i][0] == k[0]:x.keys.pop(i)returnif i < len(x.keys) and x.keys[i][0] == k[0]:return self.delete_internal_node(x, k, i)elif len(x.children[i].keys) >= self.t:self.delete_entry(x.children[i], k)else:if i != 0 and i + 2 < len(x.children):if len(x.children[i - 1].keys) >= self.t:self.move_right(x, i)elif len(x.children[i + 1].keys) >= self.t:self.move_left(x, i)else:self.merge(x, i)self.delete_entry(x.children[i], k)def delete_internal_node(self, x, k, i):if x.leaf:if x.keys[i][0] == k[0]:x.keys.pop(i)returnif len(x.children[i].keys) >= self.t:x.keys[i] = self.find_predecessor(x.children[i])self.delete_entry(x.children[i], x.keys[i])elif len(x.children[i + 1].keys) >= self.t:x.keys[i] = self.find_successor(x.children[i + 1])self.delete_entry(x.children[i + 1], x.keys[i])else:self.merge(x, i)self.delete_entry(x.children[i], k)def find_predecessor(self, x):while not x.leaf:x = x.children[-1]return x.keys[-1]def find_successor(self, x):while not x.leaf:x = x.children[0]return x.keys[0]def move_right(self, x, i):child = x.children[i]sibling = x.children[i + 1]child.keys.append(x.keys[i])x.keys[i] = sibling.keys[0]sibling.keys.pop(0)if not child.leaf:child.children.append(sibling.children[0])sibling.children.pop(0)def move_left(self, x, i):child = x.children[i]sibling = x.children[i - 1]child.keys.insert(0, x.keys[i - 1])x.keys[i - 1] = sibling.keys[-1]sibling.keys.pop()if not child.leaf:child.children.insert(0, sibling.children[-1])sibling.children.pop()def merge(self, x, i):child = x.children[i]sibling = x.children[i + 1]child.keys.append(x.keys[i])child.keys += sibling.keyschild.children += sibling.childrenx.keys.pop(i)x.children.pop(i + 1)

现在,我们可以使用以下代码测试B树的删除操作:

tree = BTree(3)
tree.insert((10, 'a'))
tree.insert((20, 'b'))
tree.insert((30, 'c'))
tree.insert((40, 'd'))
tree.insert((50, 'e'))tree.delete((20, 'b'))

这将删除关键字 (20, ‘b’) 并自动调整B树以保持平衡。最终的B树如下所示:

          (30, 'c')/     \(10, 'a')   (40, 'd')\(50, 'e')

在这个示例中,我们实现了B树的删除操作,包括查找要删除的关键字、用后继或前驱节点替换要删除的关键字、递归地删除后继或前驱节点以及维护B树的平衡。这些操作确保了B树在插入、删除和查找操作中始终具有对数时间复杂度,使其成为处理大量数据和外部存储的理想数据结构。

B树的性质可以通过一个表格的形式来总结,这样可以清晰地列出B树的关键特征。以下是一个关于B树性质的表格:

特性描述
定义B树是一种自平衡的多路搜索树,用于快速查找、插入和删除操作。
阶数B树的阶数 m 定义了每个节点最多有多少个子节点。
最小度数B树的最小度数 t,等于 m/2 向上取整。
关键字数量每个节点最多有 m-1 个关键字,至少有 t-1 个关键字(对于非根节点)。
子节点数量每个节点至少有 t 个子节点(对于非根节点),最多有 m 个子节点。
根节点根节点至少有一个关键字,最多有 m-1 个关键字。
叶子节点所有叶子节点都位于同一层,并且不含有子节点。
关键字顺序节点中的关键字按升序排列。
分割规则当一个节点的关键字数量超过 m-1 时,节点将被分割。
分割细节分割时,中间关键字被提升到父节点,形成两个新的子节点。
平衡性B树的所有路径从根到叶的长度相同。

此外,下面是关于B树插入、删除和查找操作的表格:

操作描述
插入当插入一个关键字时,如果节点未满,则直接插入;如果节点已满,则节点将被分割,中间关键字提升到父节点。
删除删除一个关键字可能涉及替换内部节点的关键字(用前驱或后继),然后从叶节点删除该关键字。如果删除导致节点的关键字数量低于 t-1,则需要与兄弟节点合并或重新分配关键字。
查找从根节点开始,比较关键字,向下遍历到正确的子节点,直到找到关键字或到达叶节点。

这些表格提供了B树的基本结构和操作的概览,帮助理解其设计和功能。B树的设计目的是为了优化磁盘I/O,因为磁盘读写通常涉及较大的块,B树允许在每次磁盘访问时读取更多的数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/41731.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【】AI八股-神经网络相关

Deep-Learning-Interview-Book/docs/深度学习.md at master amusi/Deep-Learning-Interview-Book GitHub 网上相关总结&#xff1a; 小菜鸡写一写基础深度学习的问题&#xff08;复制大佬的&#xff0c;自己复习用&#xff09; - 知乎 (zhihu.com) CV面试问题准备持续更新贴 …

.net 调用海康SDK的跨平台解决方案

📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:新的征程,我们面对的不仅仅是技术还有人心,人心不可测,海水不可量,唯有技术,才是深沉黑夜中的一座闪烁的灯塔序言 上2篇海康SDK使用以及常见的坑…

PCL 点云PFH特征描述子

点云PFH特征描述子 一、概述1.1 概念1.2 算法原理一、代码实现二、结果示例一、概述 1.1 概念 点特征直方图PFH(Point Feature Histograms)描述子:用于表示点云中每个点的局部几何形状信息,它是一种直方图描述子,包括了点云的法线方向和曲率信息,PFH描述子可以帮助区分不同…

深入Django(八)

掌握Django的管理后台 引言 在前七天的教程中&#xff0c;我们介绍了Django的基础架构、模型、视图、模板、URL路由、表单系统以及数据库迁移。今天&#xff0c;我们将深入了解Django的管理后台&#xff0c;这是一个功能强大的内置管理界面&#xff0c;用于创建、更新、查看和…

【JavaEE精炼宝库】文件操作(1)——基本知识 | 操作文件——打开实用性编程的大门

目录 一、文件的基本知识1.1 文件的基本概念&#xff1a;1.2 树型结构组织和目录&#xff1a;1.3 文件路径&#xff08;Path&#xff09;&#xff1a;1.4 二进制文件 VS 文本文件&#xff1a;1.5 其它&#xff1a; 二、Java 操作文件2.1 方法说明&#xff1a;2.2 使用演示&…

QT面试笔记总计

一 Qt 保证多线程安全? 使互斥锁保证多线程安全性。QMutex类、。使用读写锁保证多线程安全性&#xff0c;QReadWriteLock。使用信号和槽机制保证多线程安全性。使用显示切换保证多线程安全性。QTread类。 Qt 中的事件与信号的区别? 事件与信号的实现机制不同&#xff1b;事…

HCIA综合实验

学习新思想&#xff0c;争做新青年。今天学习的是HCIA综合实验&#xff01; 实验拓扑 实验需求 总部&#xff1a; 1、除了SW8 SW9是三层交换机&#xff0c;其他交换机均为2层交换机。 2、GW为总部的出口设备&#xff0c;使用单臂路由技术&#xff0c;VLAN10,20,100的网关都在GW…

ERROR: “armeabi-v7a“ not supported for HarmonyOS

IDE 从 devecostudio-mac-4.1.3.700 升级至 devecostudio-mac-5.0.3.403 后抛出了如下异常: ERROR: "armeabi-v7a" not supported for HarmonyOS. 解决办法 一.entry/build-profile.json5 需 entry/build-profile.json5 的 abiFilters 中移除 "armeabi-v7a&qu…

计算机网络体系结构详解:协议与分层

在学习计算机网络时&#xff0c;理解网络协议与分层体系结构是至关重要的。本文将详细介绍这些概念&#xff0c;帮助基础小白快速入门。 1. 什么是网络协议 网络协议是计算机网络中用于数据交换的规则和标准。这些规则规定了数据格式、时序以及发送和接收数据时的动作。网络协…

Unity3D瓦片地图辅助定位工具

介绍 该工具用于TileMap的瓦片辅助定位&#xff0c;通过键盘或鼠标按瓦片尺寸0到1的比例作为单次移动值移动定位点游戏对象。当采用定位点游戏对象映射瓦片时&#xff0c;可使用该工具来移动定位点游戏对象&#xff0c;在新版本Unity3D的TileMap编辑器中可使用GameObject Brush…

基于java+springboot+vue实现的流浪动物管理系统(文末源码+Lw)277

摘 要 在如今社会上&#xff0c;关于信息上面的处理&#xff0c;没有任何一个企业或者个人会忽视&#xff0c;如何让信息急速传递&#xff0c;并且归档储存查询&#xff0c;采用之前的纸张记录模式已经不符合当前使用要求了。所以&#xff0c;对流浪动物信息管理的提升&…

【React】React18 Hooks之useState

目录 useState案例1&#xff08;直接修改状态&#xff09;案例2&#xff08;函数式更新&#xff09;案例3&#xff08;受控表单绑定&#xff09;注意事项1&#xff1a;set函数不会改变正在运行的代码的状态注意事项2&#xff1a;set函数自动批量处理注意事项3&#xff1a;在下次…

实现基于Spring Security的权限管理系统

实现基于Spring Security的权限管理系统 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在现代Web应用中&#xff0c;权限管理系统是至关重要的组成部分。通过…

[数据集][目标检测]护目镜检测数据集VOC+YOLO格式888张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;888 标注数量(xml文件个数)&#xff1a;888 标注数量(txt文件个数)&#xff1a;888 标注类别…

ORB 特征点提取

FAST关键点 选取像素p&#xff0c;假设它的亮度为Ip&#xff1b; . 设置一个阈值T&#xff08;比如Ip的20%&#xff09;&#xff1b; 以像素p为中心&#xff0c;选取半径为3的圆上的16个像素点&#xff1b; 假如选取的圆上&#xff0c;有连续的N个点的亮度大于IpT或小于…

Redis 八股文

标题 1. Redis主从同步原理&#xff1a;判断下线的条件:故障转移如何保证Sentinel高可用 1. Redis主从同步原理&#xff1a; 1、slave执行命令向master建立连接 2、master执行bgsave&#xff08;后台存储&#xff09;&#xff0c;生成rdb快照&#xff08;redis备份方式&#x…

FreeRTOS中vTaskDelay 和 xTaskDelayUntil 的区别?

vTaskDelay 和 xTaskDelayUntil 是 FreeRTOS 提供的两种不同任务延迟函数&#xff0c;各自有其适用的场景和优缺点。vTaskDelay 适用于简单的延迟操作&#xff0c;而 xTaskDelayUntil 提供了精确的周期控制能力。在设计 FreeRTOS 应用程序时&#xff0c;根据任务的时间要求选择…

日志自动分析-Web---360星图GoaccessALBAnolog

目录 1、Web-360星图(IIS/Apache/Nginx) 2、Web-GoAccess &#xff08;任何自定义日志格式字符串&#xff09; 源码及使用手册 安装goaccess 使用 输出 3-Web-自写脚本&#xff08;任何自定义日志格式字符串&#xff09; 4、Web-机器语言analog&#xff08;任何自定义日…

游戏AI的创造思路-技术基础-强化学习(1)

我们“强化”一下机器的“学习”&#xff0c;让机器变得更强~~~~ 目录 1. 强化学习的定义 2. 发展历史 3. 强化学习的基本概念和函数 3.1. 基本概念和函数 3.1.1. 基本概念和函数 3.1.2. Q函数 3.1.2.1. 定义与作用 3.1.2.2. 数学表示 3.1.2.3. 更新规则 3.1.2.4. 算…

AI时代算法面试:揭秘高频算法问题与解答策略

三种决策树算法的特点和区别 ID3算法&#xff1a;基本的决策树算法&#xff0c;适用于简单的分类问题C4.5算法&#xff1a;改进了ID3算法&#xff0c;适用于更复杂的分类问题&#xff0c;可以处理连续型数据和缺失值CART算法&#xff1a;更加通用的决策树算法&#xff0c;适用于…