数据结构和算法学习笔记十五:多路查找树(B树)
一.概念
1.多路查找树(multi-way search tree):所谓多路,即是指每个节点中存储的数据可以是多个,每个节点的子节点数也可以多于两个.使用多路查找树的意义在于有效降低树的深度,从而降低查找深度.
2.2-3树:2-3树是指满足以下条件的多路查找树:1)每个节点可以是2节点(包含一个元素和2个子节点)或者3节点(包含两个元素和3个子节点);2)一个2节点要么没有子节点,要么有两个子节点,不存在只有一个子节点的情况;3)一个3节点同理,要么没有子节点,要么有3个子节点,且3节点的元素必须是两个.
3.2-3-4树:相对于2-3树,还拓展了4节点(有3个元素和4个子节点),4节点同样需要满足条件:要么没有子节点,要么有4个子节点,其中的元素必须是3个.
4.B树:这是一种平衡的多路查找树,2-3树和2-3-4树都是特殊的B树,其中最多子节点的节点的子节点数称为B树的阶.
5.B+树:对B树的优化,在B+树中每个叶子节点还会记录下一个叶子节点的指针.B+树对B树的查找进行了优化,特别适合带有范围的查找,且插入和删除都必须在叶子节点上进行.
二.2-3树
1.2-3树的存储规则
1)我们以一个实际的2-3树为例,如下图所示:
我们可以看到,在这颗树中根节点8元素节点是2节点,2节点要么没有子树要么有两颗子树,2节点的存储方法和普通的二叉排序树相同,左子树中节点值都比当前节点的值小,右子树中的节点的值都比当前节点值要大;值得注意的是3节点,如图中的12\14元素节点,3节点要么没有子树,要么有3颗子树,且有3颗子树时同样要满足二叉排序树的定义,也就是说左子树中的节点值都比3节点中的最小值小(图中9\10的值比12小),中子树的所有节点的值都介于3节点中的两个值之间(图中13介于12和14之间),右子树中节点的值都比3节点中最大值要大(图中15比14大).最后需要注意,图中的所有叶子节点都在同一层,这也是2-3树存储时需要满足的规则.
借助上图我们直观梳理了2-3树的存储规则,那么接下来我们看一下如何向2-3树中添加元素.
2.二叉树的插入
情况一:我们插入元素3,首先和根节点8比较,3比8小,向8的左子树插入,--->3比4小,向4的左子树插入,--->下面一个节点是一个叶子结点同时也是一个2节点,需要比较吗?显然不需要,因为如果将3元素节点作为1元素节点的子节点,不仅1节点只有一个子节点,不满足定义,而且所有叶子节点也不都在同一层,同样不满足定义.当插入时遇到叶子节点同时这个节点是2节点时,直接将这个2节点变为3节点即可,插入结果如下图所示:
情况二:我们继续再插入一个元素5,首先和8比较,5比8小,向8的左子树插入,--->5比4大,向4的右子树插入,--->插入到6\7元素节点中时出现了问题,这是一个3节点,无法插入,但是也不能将5作为6\7元素节点的子节点,原因自不用赘述,那么应该怎么办呢?我们可以这样做,先不管这是一个3节点,自顾自地将5插入其中,结果如下图所示:
插入后的5\6\7元素节点显然是不符合定义的,3个元素多了,我们需要拿出一个元素来形成一个新的节点,而且这个节点不能向下插入,怎么办呢?相信聪明的你已经想到了,向下不可以我们向上挪一个元素,4元素节点是一个2节点,多一个元素变成3节点也是可以的.但是这里又有了其他问题:哪个元素可以向上挪呢?4元素节点这个2节点变成3节点后需要有3颗子树,但是现在只有两颗子树,怎么办呢?第二个问题不难解决,现在的5\6\7元素节点有3个元素,挪动一个上去后将剩下的两个元素拆开,形成两个2节点即可,所以第一个问题也就解决了,一定是挪动5\6\7元素节点里面中间的元素6上去,因为3元素节点的中子树的所有元素值都要介于这个节点的两个值之间,不信的话你可以挪动5或7上去试一试.好了,现在我们经过对2-3树中部分节点的元素相互移动和节点拆分合并,成功将元素5插入了,结果如下图所示:
总结一下这次插入操作:元素插入时一定是插入叶子结点中的,当即将插入的节点是一个2节点时,直接插入即可,当要插入的节点是一个3节点时,先不管不顾地插入,然后将插入后节点的3个元素中中间的元素向上挪动,并将剩下的两个元素拆分成两个节点.
诶,是不是又有问题了,如果挪上去发现上面的节点还是3节点怎么办呢?我们来看下面一个例子:
情况三:插入元素11,显然经过比较(省略比较过程)应当插入9\10元素节点中,先插入,如下图所示:
接下来按照上一次的步骤我们将9\10\11这三个元素中中间的10元素向上挪,并将剩下的元素9和11拆开,结果如下图所示:
我们会发现,元素10挪上去时原来的12\14元素节点本来就是3节点,现在有3个元素了,然后将元素9和元素11拆开形成两个节点后,这个10\12\14节点就有4个子节点了(本来就有3个,又拆出来一个),那么接下来怎么办?还用问么,继续向上挪呗!将10/12/14这三个元素中的中间元素12继续向上挪,并将剩下的10和14拆开,就形成了两个2节点,诶,不是刚好有4个子节点吗?一切都是那么完美.继续挪动后形成了如下图所示的2-3树,11元素的插入就算完成了:
总结:如果你一直跟着我的思路读到这里的话,相信你已经发现了一些规律了.当我们向2-3树中插入元素时,一定是向叶子节点中插入的.如果这个叶子节点是2节点,直接插入将其变为3节点即可.如果这个叶子节点已经是3节点了,先插入,然后将3个元素中中间的元素挪给父节点并将剩下的两个元素拆分成两个节点,如果父节点是2节点的话,父节点就会变成3节点,如果父节点已经是3节点的话,同样先将挪上去的元素放入,然后将3个元素中中间的元素继续向父节点挪动并将剩下的两个元素拆分为两个节点,这个父节点刚好有4个子节点就可以分配给这两个2节点了...如果父节点的父节点还是3节点的话,不用说,继续向上挪动元素,直到遇到某个父节点是2节点停止或者到达根节点.很自然地我们会想到,如果一直挪到根节点并且根节点也刚好是3节点呢?有兴趣的话可以继续将元素2插入上图中的2-3树中试一试,就会出现这种情况.其实不难解决,如果将元素放入根节点中并将根节点此时的3个元素的中间元素向上挪动形成新的根节点,将剩下的两个元素拆分为两个节点刚好可以作为新根节点的子节点.其实这就是2-3树的深度增长的情况了.从这里可以看出来,2-3树的深度的增长不是向下增长的,而是向上增长的.
总的来说,2-3树的插入方法可以用一个"挤"字总结,插入时直接插入节点中,如果节点中的元素个数达到3个,挤不下了,就将中间的元素向上挤并将剩下两个元素拆成两个节点,如果上一层的节点还挤不下,继续向上挤并将剩下的元素拆分......
3.2-3树的删除实现
2-3树的删除有很多形式,我们可以看一看下面的例子总结如何进行删除:
情况一:删除下面2-3树中的节点6
我们可以发现,将6删除后4\6元素节点由3节点变成了2节点,那么子节点的个数必须减少1,我们很容易想到可以将5元素节点和7元素节点合并,结果如下图:
情况二:删除下面2-3树中的元素40:
显然,删除元素40后,原来的20\40元素节点由3节点变成了2节点,所以它的子节点的数目应该减少1,但是这里子节点都是3节点,显然不能合并,那么我们应该怎么办呢?我们可以从子节点中匀一个元素给这个节点,显然,只能匀35上去,匀完后就有了如下图的结果:
总结:我们可以在这里作一个小总结.将情况一和情况二对比,我们发现同样是3节点的一个元素被删除,情况一删除后变成了2节点,而情况二中删除后这个节点还是3节点,这是怎么回事呢?其实情况一也可以在移除完成后仍然是3节点的(读者可自行尝试看情况一能否保留3节点).这里我们用一个"借"字来总结这个移除过程:当某个元素被移除后,这个元素的位置就出现了一个空缺,那么如何去补这个空缺呢?我们可以通过借用子节点中的元素的方式来补,优先借用这个元素的相邻子节点中的元素,如情况一中的元素5和元素7,但是显然这两个元素都分别在2节点中,元素离开了这个节点就消失了,所以借不了,我们就考虑不借了,将剩下的元素合并保留4节点为2节点的形式;情况二中20\40元素节点中的元素40被移除,由于3个子节点都是3节点,所以直接借了子节点中的元素来用,当然如果子节点还有子节点,还需要继续借用元素来填补被借的元素位置或者合并元素.
那么,如果我们遇到子节点的元素借不出去又合并不了的情况怎么处理呢?
情况三:移除下面2-3图中的元素90:
显然,90元素所在节点是2节点,90元素移除后这个节点就没有元素了,必须从其他地方借用一个元素来填补或者合并节点.如果将85元素节点和95元素节点合并显然不是很合适,因为合并后将其作为80元素节点的右子节点就不满足2-3树的定义了,但是从85元素或者95元素中借用一个元素填补90元素的位置还是不满足2-3树定义.怎么办呢?是不是又想到了,子节点借不了向父节点借嘛,将80元素借用过来,然后80元素留下的空位再从它的左子树中借用一个元素来填补,首先考虑左子树中的最大元素43,这个元素被借用后又出现了空位,然后可以考虑将其父节点中的35元素借用过来填补空位,之后父元素留下的空位让25元素和35元素合并即可.结果如下图所示:
总结:我们可以看到,当一个元素被移除后,这个元素留下的空位我们可以采用借的方式来填补,首先从左右子树中去借,从子树中借一定借用的是左子树的最大元素或者右子树的最小元素.如果子树中没有元素可借,那么可以考虑合并子节点(子节点是叶子节点的话).当一个元素被借用来填补其他元素的位置,那么这个元素也可以采用同样的方案从它的子树中或者父节点中去借用元素,也可以考虑合并子节点.如果最终发现不能借用了(这种情况出现时,一定是借用了完全二叉树中的节点),这时就要考虑将树的深度降低1了(这里不再赘述如何降低树的深度).
三.2-3-4树
对于2-3-4树,这里可以不再赘述,因为2-3-4树就是2-3树的进一步拓展,其添加元素或者删除元素的过程和2-3树相同.
四.B树:
B树是平衡的多路查找树,2-3树和2-3-4树都是B树的特例.
五.B+树:
我们在说到2-3树时有讲到在2-3树中所有叶子节点都在同一层,这也是所有B树都需要满足的条件.如果我们在这些叶子节点中增加两个指针,指向父节点中的相应元素及下一个兄弟节点的位置,那么这样的树就是B+树.B+树特别适合有范围的查找(根据叶子结点中下一个叶子节点的指针遍历所有叶子节点,根据父节点的指针判断范围).
数据结构和算法学习笔记十五:多路查找树(B树)的更多相关文章
- python3.4学习笔记(十五) 字符串操作(string替换、删除、截取、复制、连接、比较、查找、包含、大小写转换、分割等)
python3.4学习笔记(十五) 字符串操作(string替换.删除.截取.复制.连接.比较.查找.包含.大小写转换.分割等) python print 不换行(在后面加上,end=''),prin ...
- (转载)西门子PLC学习笔记十五-(数据块及数据访问方式)
一.数据块 数据块是在S7 CPU的存储器中定义的,用户可以定义多了数据块,但是CPU对数据块数量及数据总量是有限制的. 数据块与临时数据不同,当逻辑块执行结束或数据块关闭,数据块中的数据是会保留住的 ...
- (C/C++学习笔记) 十五. 构造数据类型
十五. 构造数据类型 ● 构造数据类型概念 Structured data types 构造数据类型 结构体(structure), 联合体/共用体 (union), 枚举类型(enumeration ...
- java数据结构和算法学习笔记
第一章 什么是数据结构和算法 数据结构的概述 数据结构是指 数据再计算机内存空间或磁盘空间中的组织形式 1.数据结构的特性 数据结构 优点 缺点 数组 插入快,如果知道下标可以快速存取 查找和删除慢 ...
- javascript 数据结构和算法读书笔记 > 第五章 队列
队列是一种列表,但是它只能够在队尾插入元素,在队首删除元素.队列用于存储按照顺序排列的数据,先进先出.而栈则是后入栈的元素反而被优先处理. 实际中一般被应用在进程池.排队操作上面. 1. 队列的操作 ...
- Java基础学习笔记十五 集合、迭代器、泛型
Collection 集合,集合是java中提供的一种容器,可以用来存储多个数据. 在前面的学习中,我们知道数据多了,可以使用数组存放或者使用ArrayList集合进行存放数据.那么,集合和数组既然都 ...
- MySQL学习笔记十五:优化(2)
一.数据库性能评测关键指标 1.IOPS:每秒处理的IO请求次数,这跟磁盘硬件相关,DBA不能左右,但推荐使用SSD. 2.QPS:每秒查询次数,可以使用show status或mysqladmin ...
- angular学习笔记(十五)-module里的'服务'
本篇介绍angular中的模块:module 在笔记(二)http://www.cnblogs.com/liulangmao/p/3711047.html里已经讲到过模块,这篇主要讲模块的 '服务' ...
- Java学习笔记十五:Java中的成员变量和局部变量
Java中的成员变量和局部变量 一:成员变量: 成员变量在类中定义,用来描述对象将要有什么 成员变量可以被本类的方法使用,也可以被其他类的方法使用,成员变量的作用域在整个类内部都是可见的 二:局部变量 ...
随机推荐
- 「10.19」最长不下降子序列(DP)·完全背包问题(spfa优化DP)·最近公共祖先(线段树+DFS序)
我又被虐了... A. 最长不下降子序列 考场打的错解,成功调了两个半小时还是没A, 事实上和正解的思路很近了,只是没有想到直接将前$D$个及后$D$个直接提出来 确实当时思路有些紊乱,打的时候只是将 ...
- 源码学习之void 0
今天看源码的时候看到 void 0 这样的写法,平时在业务代码里基本没有这样的写法,于是学习了一下. 在控制台运行了一下void 0,得到返回值是undefined. 在MDN上搜了一下void,了解 ...
- text-decoration属性作用和方法
text-decoration-line(注释文本添加一条装饰线):none(文本中没有线条). underline(文本的下方显示一条线). overline(文本的上方将显示一条线). line- ...
- 最新Unity 与Android 交互通信(基于Unity 2019.4 和 Android Studio 4.1.1)
原文章链接:https://blog.csdn.net/woshihaizeiwang/article/details/115395519 CLSays:网上找了一圈,真的是很多都不能用,要么太老,要 ...
- 关于LCA的几点想法
倍增 这是最最最常见的写法了,一个fa[N][logN]的数组直接搞定 时间复杂度也不算太高 预处理 $ O(nlogn) $ 如果你想卡的话,可以卡到 $ O(nlogh) $ h为树的深度 查询 ...
- 工作3年,还不会写单元测试?新技能get!
历史遗留代码不敢重构? 每次改代码都要回归所有逻辑? 提测被打回? 在近期的代码重构的过程中,遇到了各式各样的问题.比如调整代码顺序导致bug,取反操作逻辑丢失,参数校验逻辑被误改等. 上线前需要花大 ...
- POJ 1222 高斯消元更稳
大致题意: 有5*6个灯,每个灯只有亮和灭两种状态,分别用1和0表示.按下一盏灯的按钮,这盏灯包括它周围的四盏灯都会改变状态,0变成1,1变成0.现在给出5*6的矩阵代表当前状态,求一个能全部使灯灭的 ...
- acwing 4 多重背包问题 I
多重背包 有 n种物品 一共有 m大小的背包,每种物品的价值 大小 个数 为 s[i],v[i],num[i]; #include<bits/stdc++.h>//cmhao #defin ...
- SpringBoot | 1.2 全注解下的Spring IoC
前言 在学习SpringBoot之前,有几个Spring的重要的基础概念需要提一下,SpringBoot对这些基础概念做进一步的封装,完成自动配置.首先就是Spring的控制反转IOC,由于Sprin ...
- 消息队列——kafka
原文:再过半小时,你就能明白kafka的工作原理了 会出现什么情况呢? 1.为了这个女朋友,我请假回去拿(老板不批). 2.小哥一直在你楼下等(小哥还有其他的快递要送). 3.周末再送(显然等不及). ...