数据结构和算法学习笔记十五:多路查找树(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中的成员变量和局部变量 一:成员变量: 成员变量在类中定义,用来描述对象将要有什么 成员变量可以被本类的方法使用,也可以被其他类的方法使用,成员变量的作用域在整个类内部都是可见的 二:局部变量 ...
随机推荐
- 【Azure API 管理】APIM集成内网虚拟网络后,启用自定义路由管理外出流量经过防火墙(Firewall),遇见APIs加载不出来问题
问题描述 使用 Azure 虚拟网络,Azure APIM 可以管理无法通过 Internet 访问的 API,达到以保护企业内部的后端API的目的.在虚拟网络中,启用网络安全组(NSG:Networ ...
- 01-Tkinter教程-窗口的管理与设置
Tkinter介绍 官方用的GUI工具包--Tkinter(IDLE就是用这个开发的). Tkinter是Python的标准GUI库,它实际是建立在Tk技术上的.在大多数Unix平台以及Windows ...
- Python-统计目录(文件夹)中Excel文件个数和数据量
背景:前一阵子在帮客户做Excel文件中的数据处理,但是每周提交周报,领导都需要统计从客户接收的文件数量以及记录数.所以我就简单写了统计的脚本,方便统计目录(文件夹)中的Excel文件个数和数据量. ...
- CentOS7日志管理工具 journalctl
1.简介 日志管理工具journalctl是centos7上专有的日志管理工具,该工具是从message这个文件里读取信息. Systemd统一管理所有Unit的启动日志.带来的好处就是,可以只用jo ...
- 【spring源码系列】之【Bean的实例化】
人生需要探索的热情.坚持的勇气以及热爱生活热爱自己的力量. 1. Bean的实例化 上一篇讲述了bean的生命周期,其中第一步就涉及到了bean的实例化,本文重点分析bean实例化,先进入源码中的Ab ...
- 探索颜色渐变绘制算法(基于Processing语言) 第一部分
突然间意识到连续变化的颜色在程序中是如何实现的这一问题.没错,就想有事找事,我会分好几部分慢慢探寻,其实笔者也不会,咱一起研究.ok,我们开始! 第一部分 初始部分就从官方案例来入手学习.官方给了三个 ...
- Linux指令手册 (二)
free free,显示系统中可用内存和已使用内存的数量. 语法:free [options] [target] 参数: -b: 以字节(bytes)显示内存量: -k: 以千字节(kilo)为单位显 ...
- XML:No operation was found with the name报错解决办法
当我们使用CXF动态客户端调用WebService接口容易出现如下问题:命名空间问题 Exception in thread "main" org.apache.cxf.commo ...
- python之学生信息管理系统
1 #!usr/bin/python 2 #encoding=utf-8 3 4 #1. 打印学生管理系统界面 5 def printStd(): 6 print ("*"*50) ...
- [小技巧] Windows7 半角全角快捷键 修改方法
From : http://blog.sina.com.cn/s/blog_87ab67b10100x3ww.html 转载说明:在浏览器下我们可以使用空格下翻一页,Shift + 空格上翻一页. 但 ...