数据结构(四十一)多路查找树(B树)
一、多路查找树的背景
前面所讨论的查找算法都是在内存中进行的,它们适用于较小的文件,而对于较大的、存放在外存储器上的文件就不合适了,对于此类大规模的文件,即使是采用了平衡二叉树,在查找效率上仍然较低。
如果要操作的数据集非常大,大到内存已经没办法处理了,这种情况下,对数据的处理需要不断从硬盘等存储设备中调入或调出内存页面。一旦涉及到这样的外部设备,关于时间复杂度的计算就会发生变化,访问该集合元素的时间已经不仅仅是寻找该元素所需比较次数的函数,必须考虑对硬盘等外部存储设备的访问时间以及将会对该设备做出多少次的单独访问。
二、多路查找树的定义
之前的树都是一个结点可以有多个孩子,但是它自身只存储一个元素,二叉树限制更多,结点最多只能有两个孩子。一个结点只能存储一个元素,在元素非常多的时候,就使得要么树的度非常大(结点拥有子树的个数的最大值),要么树的高度非常大,甚至两者都必须足够大才行。这就使得内存存取外存次数非常多,这显然成了时间效率上的瓶颈,因此,需要打破每一个结点只存储一个元素的限制。
多路查找树(muiti-way search tree),其每一个结点的孩子树可以多于两个,且每一个结点处可以存储多个元素。
三、2-3树
2-3树的定义:
- 其中的每一个结点都有两个孩子(2结点)或三个孩子(3结点)。
- 一个2结点包含一个元素和两个孩子(或没有孩子),且与二叉排序树类似,左子树包含的元素小于该元素,右子树包含的元素大于该元素。不过,与二叉排序树不同的是,这个2结点要么没有孩子,要有就有两个,不能只有一个孩子。
- 一个3结点包含一小一大两个元素和三个孩子(或没有孩子),一个3结点要么没有孩子,要么具有3个孩子。如果某个3结点有孩子的话,左子树包含小于较小元素的元素,右子树包含大于较大元素的元素,中间子树包含介于两元素之间的元素。
- 2-3树中所有叶子都在同一层次上。
四、2-3-4树
2-3-4树就是2-3树的拓展,包括了4结点的使用。一个4结点包含小中大三个数据元素和四个孩子(或没有孩子),一个4结点要么没有孩子,要么有4个孩子。如果某个4结点有孩子的话,左子树包含小于最小元素的元素;第二子树包含大于最小元素,小于第二元素的元素;第三子树包含大于第二元素,小于最大元素的元素;右子树包含大于最大元素的元素。
2-3-4树的插入过程(插入顺序为{7,1,2,5,6,9,8,4,3}):
2-3-4树的删除过程(删除顺序为:{1,6,3,4,5,2,9}):
五、B树
B树(B-Tree)是一种平衡的多路查找树,2-3树和2-3-4树都是B树的特例。结点最大的孩子数目称为B树的阶(order),因此,2-3树是3阶B树,2-3-4树是4阶B树。
一个m阶的B树具有如下属性:
- 如果根结点不是叶结点,则其至少有两个子树
- 每一个非根的分支结点都有k-1个元素和k个孩子,其中【m/2】≤k≤m。每一个叶子结点n都有k-1个元素,其中【m/2】≤k≤m。
- 所有叶子结点都位于同一层次
- 所有分支结点包含下列信息数据(n,A0,K1,A1,K2,A2,...,Kn,An),其中:Ki(i=1,2,...,n)为关键字,且Ki<Ki+1(i=1,2,...n-1);Ai(i=0,1,2,...n)为指向子树根结点的指针,且指针Ai-1所指子树中所有结点的关键字均小于Ki(i=1,2,...,n),An所指子树中所有结点的关键字均大于Kn,n(【m/2】- 1≤ n ≤m - 1)为关键字的个数(或n+1为子树的个数)。
在B树上查找的过程是一个顺时针查找结点和在结点中查找关键字的交叉过程。例如,要查找数字7,首先从外存(硬盘)中读取得到根结点3、5、8三个元素,发现7不在当中,但在5和8之间,因此就通过A2再读取外存的6、7结点,查找到所要的元素。
B树结构可以减少内存与外存交换数据的次数,从而提高时间效率:外存比如硬盘是将所有的信息分割成相等大小的页面,每次硬盘读写的都是一个或多个完整的页面,对于一个硬盘来说,一页的长度可能是211到214个字节。在一个典型的B树应用中,要处理的硬盘数据量很大,因此无法一次全部装入内存。因此会对B树进行调整,使得B树的阶数(该结点的元素)与硬盘存储的页面大小相匹配。比如说一棵B树的阶为1001(即1个结点包含1000个关键字),高度为2,它可以存储超过10亿个关键字,只要让根结点持久地保存在内存中,那么在这棵树上,寻找某一个关键字至多需要两次硬盘的读取即可。
通过这种方式,在有限内存的情况下,每一次磁盘的访问都可以获得最大数量的数据。由于B树每结点可以具有比二叉树多得多的元素,所以与二叉树操作不同,它们减少了必须访问结点和数据块的数量,从而提高了性能。可以说,B树的数据结构就是为内外存的数据交互准备的。
1.B树的查找算法
将key与根结点的Ki逐个顺序比较:
- 若key=Ki,则查找成功;
- 若key<Ki,则沿着指针A0所指的子树继续查找;
- 若Ki < key < Ki+1,则沿着指针Ai所指的子树继续查找;
- 若key > Ki,则沿着指针An所指的子树继续查找。
2.B树的插入算法
(1)利用B树的查找算法找出该数据元素结点应该插入的结点位置(B树的插入结点一定是叶结点)
(2)判断要插入的结点是否还有空位置,即判断该结点是否满足n<m-1(n为结点中关键字的个数,m为B树的阶,即所有结点的孩子结点个数最大值),
若该结点满足n<m-1,说明该结点还有空位置,直接把数据元素key插入到该结点的合适位置上(即插入后满足结点上的数据元素序列仍然保持有序);
若该结点满足n=m-1,说明该结点已经没有空位置,要插入就要分裂该结点。
分裂的方法是:以中间数据元素为界把结点分为两个结点,并把中间数据元素向上插入到双亲结点上,若双亲结点未满,则把它插入到双亲结点的合适位置上,若双亲结点已满,则按同样的方法继续向上分裂。这个向上分裂的过程可以一直进行到根结点的分裂。若最终根结点进行了分裂,则B树的高度将增1。
以2-3树的插入过程为例(省略了n):
- n<m-1有位置时:将3直接插入1结点中形成1-3结点
- n=m-1没有位置时,
①双亲结点未满:去5-6-7中间数据元素6插入到双亲结点4中形成4-6结点
②双亲结点已满,上一级未满时,由于9-10结点和12-14结点都满了,但是4-6结点和12-14结点的双亲没有满,所以讲9-10-11的中间元素10拿出,由于12-14结点满了,所以将12-14结点也拆分,将3结点变成2结点,从而让12到8中形成8-12结点。
③双亲结点已满,上一级已满时,由于1-3满,4-6满,8-12也满,所以讲1-3拆分,拿出2,将4-6拆分,拿出4,将8-12拆分,拿出8,然后将它们连接起来。由于根结点的分裂使得树的高度增加了。
3.B树的删除操作
B树的删除操作分为两步:
(1)利用B树的查找算法找到该数据元素所在的结点
(2)在结点上删除数据元素key分为两种情况:一种是在叶结点上删除数据元素;另一种是在非叶结点上删除数据元素。
- 在非叶结点上删除数据元素的算法思想:假设要删除一个结点的数据元素Ki(1≤i≤n),首先寻找该结点Ai所指子树中的最小元素Kmin(Ai所指子树中的最小数据元素Kmin一定为叶结点上),然后用kmin覆盖要删除的数据元素Ki,最后再以指针Ai所指结点为根结点查找并删除Kmin(即再以Ai所指结点为B树的根结点,以Kmin为要删除数据元素再次调用B树上的删除算法)。这样就把非叶结点上的删除问题转化成了叶结点上的删除问题。
- 在B树的叶结点上删除数据元素共有以下三种情况:
- ①假如要删除的数据元素结点的数据元素个数n≥【m/2】,即说明删去该数据元素后,该结点仍满足B树的定义,则可以直接删去该数据元素。
- ②假如要删除的数据元素结点的数据元素个数n=【m/2】- 1,即说明删去该数据元素后,该结点将不满足B树的定义,并且该结点的左(或右)兄弟结点中有数据元素个数n>【m/2】- 1,则把该结点的左(或右)兄弟结点中最大(或最小)的数据元素上移到双亲结点中,同时把双亲结点中大于(或小于)上移数据元素的数据元素下移到要删除数据元素的结点中,这样删去数据元素后该结点以及它的左(或右)兄弟结点都仍然满足B树的定义。
- ③假如要删除的数据元素结点的数据元素个数n=【m/2】- 1,即说明删去该数据元素后,该结点将不满足B树的定义,并且该结点的左(或右)兄弟结点中的数据元素个数n均等于【m/2】- 1,则把要删除数据元素的结点与其左(或右)兄弟结点以及双亲结点中分割二者的数据元素合并成一个结点。
以2-3树的删除过程为例(省略了n,m=3,【m/2】= 2):
①在叶结点删除数据元素的情况①(所删除元素位于一个3结点的叶子结点上):n=2,将数据元素9直接删除,不会影响整棵树的其他结构。
②在叶结点删除数据元素的情况②(所删除的元素位于一个2结点的叶子结点上,且此结点的双亲也是2结点,且拥有一个3结点的右孩子):n=1,6-7结点个数为2>【m/2】- 1,将6-7结点中最小的上移到4结点中,并将4下移到1结点中。
③在叶结点删除数据元素的情况③(所删除的元素位于一个2结点的叶子结点上,且此结点的双亲是3结点):n=1,其右兄弟13结点和15结点的数据元素个数均为1=【m/2】- 1,则将右兄弟13结点和双亲结点中的12合并成一个结点12-13结点。
还有一种情况(所删除的元素位于一个2结点的叶子结点上,且当前树是一个满二叉树):如果当前树是一个满二叉树,n=1,其左兄弟6结点的数据元素个数为1=【m/2】- 1,将其左兄弟6和双亲结点7合并成一个结点6-7,同时需要调整此时B树的结构,将此时6-7结点的右兄弟和其双亲9合并成一个结点9-14。
还有一种情况(所删除的元素位于一个2结点的叶子结点上,且此结点的双亲也是2结点,且拥有一个2结点的右孩子):此时删除结点4,如果直接左旋会造成没有右孩子,因此需要对整棵树变形,目标就是让结点7变成3结点,于是让8下移,并且9上移补充8的位置,然后再删除结点4即可。
④在非叶结点上删除数据元素的情况(也可以将树按照中序遍历后得到此元素的前驱或后继元素,然后让它们来补位即可):
(所删除的元素位于一个2结点的分支结点上)让1去覆盖4,然后以4为B树的根结点,删除叶结点中的1。
(所删除的元素位于一个3结点的分支结点上)让9去覆盖12,然后以9-14为B树的根结点,删除叶结点中的9。
六、B+树
B树还是有缺陷的,对于树结构来说,可以通过中序遍历来顺序查找树中的元素,这一切都是在内存中进行。但是在B树中,往返于每个结点就意味着,必须得在硬盘的页面之间进行多次访问,例如,遍历下面这棵B树,假设每个结点都属于硬盘的不同页面,中序遍历所有元素就需要访问:页面2→页面1→页面3→页面1→页面4→页面1→页面5。即每次经过结点遍历时,都会对结点中的元素进行一次遍历,如何让遍历时每个元素只访问一次就成了需要解决的问题。
B+树是在原有的B树结构基础上,加上了新的元素组织方式。B+树是应文件系统所需而出的一种B树的变形树,在B树中,每一个元素在该树中只出现一次,有可能在叶子结点上,也有可能在分支结点上。而在B+树中,出现在分支结点中的元素会被当做它们在该分支结点位置的中序后继者(叶子结点)中再次列出。另外,每一个叶子节点都会保存一个指向后一叶子结点的指针。
一棵m阶的B+树和m阶的B树的差异在于:
- 有n棵子树的节点中包含有n个关键字
- 所有的叶子结点包含全部关键字的信息,即指向含这些挂念自记录的指针,叶子结点本身按照关键字的大小自小到大顺序链接
- 所有分支结点可以看成索引,结点中仅含有其子树中的最大(或最小)关键字。
这样的数据结构最大的好处就在于,如果是要随机查找,就只需要从根结点出发,与B树的查找方式相同。只不过即使在分支结点找到了待查找的关键字,它也只是用来索引的,不能提供实际记录的访问,还是需要到达包含次关键字的终端结点。
如果需要从最小关键字进行从小到大的顺序查找,就可以从最左侧的叶子结点出发,不经过分支结点,而是延着指向下一叶子的指针就可以遍历所有的关键字。B+树的结构特别适合带有范围的查找,可以先从根结点出发找到要查找范围的最小值,然后再在叶子结点按顺序找到符合范围的所有记录即可。
n<m-1
数据结构(四十一)多路查找树(B树)的更多相关文章
- B树,B+树比较
首先注意:B树就是B-树,"-"是个连字符号,不是减号.也就是B-树其实就是B树 B-树是一种平衡的多路查找(又称排序)树,在文件系统中有所应用.主要用作文件的索引.其中的B就表示 ...
- 数据结构(六)查找---多路查找树(B树)
B 树 B树与B+树 一:定义 B树(B-树)是一种平衡的多路查找树.-3树和2--4树都是B树的特例.节点最大的孩子数组称为B树的阶(order),因此,-3树是3阶B树,--4树是4阶B树. 二: ...
- 数据结构和算法学习笔记十五:多路查找树(B树)
一.概念 1.多路查找树(multi-way search tree):所谓多路,即是指每个节点中存储的数据可以是多个,每个节点的子节点数也可以多于两个.使用多路查找树的意义在于有效降低树的深度,从而 ...
- 多路查找树之2-3-4树和B树 - 数据结构和算法82
多路查找树之2-3-4树和B树 让编程改变世界 Change the world by program 由2-3树到2-3-4树 ...... 省略,具体请看视频讲解 ...... B树 一个m阶的B ...
- Java数据结构(十五)—— 多路查找树
多路查找树 二叉树和B树 二叉树的问题分析 二叉树操作效率高 二叉树需要加载到内存,若二叉树的节点多存在如下问题: 问题1:构建二叉树时,需多次进行I/O操作,对与速度有影响 问题2:节点海量造成二叉 ...
- 数据结构(六)查找---多路查找树(B+树)
前提 下图B树,我们要遍历它,假设每个节点都属于硬盘的不同页面,我们为了中序遍历所有的元素,页面2-页面1-页面3-页面1-页面4-页面1-页面5.而且我们每经过节点遍历时,都会对节点中的元素进行一次 ...
- HTTP协议漫谈 C#实现图(Graph) C#实现二叉查找树 浅谈进程同步和互斥的概念 C#实现平衡多路查找树(B树)
HTTP协议漫谈 简介 园子里已经有不少介绍HTTP的的好文章.对HTTP的一些细节介绍的比较好,所以本篇文章不会对HTTP的细节进行深究,而是从够高和更结构化的角度将HTTP协议的元素进行分类讲 ...
- 数据结构和算法(Golang实现)(30)查找算法-2-3-4树和普通红黑树
文章首发于 阅读更友好的GitBook. 2-3-4树和普通红黑树 某些教程不区分普通红黑树和左倾红黑树的区别,直接将左倾红黑树拿来教学,并且称其为红黑树,因为左倾红黑树与普通的红黑树相比,实现起来较 ...
- 数据结构和算法(Golang实现)(29)查找算法-2-3树和左倾红黑树
某些教程不区分普通红黑树和左倾红黑树的区别,直接将左倾红黑树拿来教学,并且称其为红黑树,因为左倾红黑树与普通的红黑树相比,实现起来较为简单,容易教学.在这里,我们区分开左倾红黑树和普通红黑树. 红黑树 ...
随机推荐
- 基于操作系统原理的Linux 的基本操作和常用命令的使用
一.实验目的 1.学会不同Linux用户登录的方法. 2.掌握常用Linux命令的使用方法. 3.了解Linux命令中参数选项的用法和作用. 二.实验内容 1. 文件操作命令 (1) 查看文件与目录 ...
- 讨论c/c++计算小数的精度问题
求出所有100以下整数与一位小数相乘等于相加的浮点数这个有Bug浮点数计算时精度会出现误差 除非使用非常精确的类型或限制浮点的位数 比如 #include <iostream> int m ...
- Linux系列之yum安装
yum是Linux系统的安装必备神器,简直不要太方便.但是新系统一般是不自带yum工具的,所以需要手动安装一下. 环境:centos7 新建一个目录用来保存yum安装包 mkdir install 进 ...
- redis查找大key
redis中查找出比较大的key 下面直接上代码 (请在测试机上测试) #!/usr/bin/env python import sys import redis def check_big_key( ...
- 鱼和熊掌可兼得?一文看懂又拍云 SCDN
转眼已是 9102 年,参与工作多年的二狗子凭借他聪明的脑瓜和孜孜不倦的钻研精神,成为了某中型企业的资深网站管理员.不同于一般的"网管",二狗子自然是业内最优秀的那一类. 但是,最 ...
- 一致性hash (PHP)
<?php /** * Flexihash - A simple consistent hashing implementation for PHP. * * The MIT License * ...
- ElasticSearch Cardinality Aggregation聚合计算的误差
使用ES不久,今天发现生产环境数据异常,其使用的ES版本是2.1.2,其它版本也类似.通过使用ES的HTTP API进行查询,发现得到的数据跟javaClient API 查询得到的数据不一致,于是对 ...
- springboot配置ehcache2.X缓存(@Cacheable等注解和手动操作缓存的工具类 支持element粒度的时间设置)
本文只写出一些注意事项和源码,请善用官方文档~ 注解实现 @Cacheable @CachePut @CacheEvit 启动类上加@EnableCaching就可以开启缓存 由文档可知,自动检测缓存 ...
- spring Bean的三种配置方式
Spring Bean有三种配置方式: 传统的XML配置方式 基于注解的配置 基于类的Java Config 添加spring的maven repository <dependency> ...
- Go语言及Beego框架环境搭建
在开始环境搭建之前,我们先一起来看看: Go有什么优势: 不用虚拟机,它可直接编译成机器码,除了glibc外没有其他外部依赖,部署十分方便,就是扔一个文件就完成了. 天生支持并发,可以充分的利用多核, ...