B树--B+树原理及操作(插入,删除)

前言:

B树代码戳这里:https://blog.csdn.net/alzzw/article/details/97663352

    在讲B树之前,先说一下多路查找树:

    2-3树:是一种多路查找树:2和3的意思就是2-3树包含两种结点

(1)2结点包含一个元素和两个孩子(或者没有孩子)。

  • 左子树包含结点的元素值小于该结点的元素值,右子树包含的结点的元素值大于该结点的元素值
  • 2结点要不有两个孩子,要不就没有孩子,不允许有一个孩子

(2)3结点包含一大一小两个元素和三个孩子(或者没有孩子)。(两个元素按大小顺序排列好)

  • 左子树包含的结点的元素值小于该结点较小的元素值,右子树包含的结点的元素值大于该结点较大的元素值,中间子树包含的结点的元素值介于这两个元素值之间。
  • 3结点要不有三个孩子,要不就没有孩子,不允许有一个或两个孩子

(3)2-3树所有结点都在同一层次

  • 其中结点10就是一个2结点,因为它有一个值,两个孩子
  • 结点(12,15)就是一个3结点,它有两个值,三个孩子

2-3-4树:也是一种多路查找树:2和3和4的意思就是2-3-4树包含三种结点

2-3-4树是在2-3树的基础上多了一个4结点

(3)4结点包含小中大三个元素和四个孩子(或者没有孩子)。

  • 最左子树包含的结点的元素值小于该结点最小的元素值,第二个子树包含的结点的元素值大于最小的元素值小于中间元素值,第三个子树包含的结点的元素值大于中间元素值小于最大元素值最右子树包含的结点的元素值大于该结点最大的元素值。
  • 4结点要不有四个孩子,要不就没有孩子,不允许有一个或两个或三个孩子

 

B树:

B树:也是一种平衡的多路查找树,2-3树和2-3-4树都是B树的特例,我们把树中结点最大的孩子数目称为B树的阶。通常记为m。

一棵m阶B树或为空树,或为满足如下特性的m叉树:
1)树中每个结点至多有m棵子树。(即至多含有m-1个关键字)("两棵子树指针夹着一个关键字”)

2)若根结点不是终端结点,则至少有两棵子树。(至少一个关键字)

3)除根结点外的所有非叶结点至少有m/2棵子树。(即至少含有[m/2]-1个关键字)

4)所有非叶结点的结构如下:

  • 其中Ki(i=1,2,...,n)为结点的关键字,且满足K<K2<K3<......<kn
  • 其中Pi(i=0,1,....,n)为指向子树根结点的指针,且指针P(i-1)所指的子树的所有结点的关键字都小于Ki, Pi所指向子树的所有结点的关键字都小于K(i+1)n是结点中关键字的个数

5)所有的叶子结点出现在同一层次上,不带信息。(就像是折半查找判断树中查找失败的结点)

 

B树的操作:

 

查找:

B树是多路查找树,二叉排序树是二路查找,B树是多路查找,所以它是二又排序树的拓展。因此,B树的查找操作和二叉排序树的查找操作非常类似

查找过程:①先让待查找关键字key和结点的中的关键字比较,如果等于其中某个关键字,则查找成功。
                  ②如果和所有关键字都不相等,则看key处在哪个范围内,然后去对应的指针所指向的子树中查找
 Eg:如果Key比第一个关键字K1还小,则去P0指针所指向的子树中查找,如果比最后一个关键字Kn还大,则去Pn指针所指向的子树中查找

下面我们举一个栗子:

在该树中查找key= 5,14,11,25

  • 当key=5时,5<10,所以到10的左子树查找,查找到5=5,返回;
  • 当key=14时,14>10,所以到10的右子树查找,10的右子树有两个元素,发现14等于第一个元素,返回;
  • 当key=11时,11>10,右子树,发现11小于(14,20)这棵树的最左边的值14,所以到(14,20)的左子树,查找到11,返回;
  • 当key=25时,25>10,25>20,25>22,但是22是22是叶子结点它的右子树指向NULL,所以没有找到25这个元素;

插入:

在二叉排序树中,仅需查找到需插入的终端结点的位置。但是,在B树中找到插入的位置后,并不能简单地将其添加到终端结点位置,因为此时可能会导致整棵树不再满足B树中定义中的要求

思考:


给定一组关键字{20,30,50,52,60,68,70},给出创建一棵3阶B树的过程。


第①步:由于m=3所以除了根结点以外,非叶子结点至少有【3/2】-1=1个关键字(向上取整),最多有3-1=2个关键字。
所以依次插入20和30两个关键字到结点。

                                                                                                                        
第②步:接下来插入50,如图,但是由于最多有2个关键字,所以这个结点不满足B树要求,需要分裂

                                     

                 分裂的方法:取这个关键字数组中的中间关键字(n/2)(向上取整,以下不再说了)作为新的结点,然后其他关键字形成两个结点作为新结点的左右孩子。

第③步:接下来插入52,由于50结点只有一个关键字,所以可以插入52

                                          

第④步:接下来插入60,插入60之后该结点关键字数量又不符合要求,需要分裂

                              

                分裂过程:取中间关键字(3/2=2)52,由于根结点只含30一个关键字,可以将52和30合并到一起。
接下来需要处理50和60这两个结点,由于30<50<52,60>52,所以50和60各自单独作为一个结点

第⑤步:接下来插入68,由于60结点只有一个关键字,所以可以插入68

                                              

第⑥步:接下来插入70,插入70之后该结点关键字数量又不符合要求,需要分裂

                                               

第⑦步:分裂根结点,类似的,取中间关键字(【3/2】=2)52作为新的根结点的关键字

                                                 

删除:

B树中的删除操作与插入操作类似,但要稍微复杂些,要使得删除后的结点中的关键字个数≥「m/2]-1(向上取整),因此将涉及结点的“合并”问题。由于删除的关键字位置不同,可以分为关键字在终端结点不在终端结点上两种情况。

                    
1)如果删除的关键字在终端结点上(最底层非叶子结点)

①结点内关键字数量大于「m/2]-1,这时删除这个关键字不会破坏B树的定义要求。所以直接删除。

  • 如删除图中的9;

②结点内关键字数量等于「m/2]-1,并且其左右兄弟结点中存在关键字数量大于「m/2]-1的结点,则去兄弟阶段中借关键字。

  • 删除结点2,删除2从右兄弟借结点,并且需要按照大小顺序进行调整。

                                                                                
③结点内关键字数量等于  [m/2]-1,并且其左右兄弟结点中不存在关键字数量大于[m/2]-1的结点,则需要进行结点合并。

  • 第③种情况:删除16,左右兄弟结点都只有一个关键字,所以需要进行结点合并
  • 合并:上一层的结点取关键字与下一层的结点合并。
    方式不唯一,可以从把关键字14取下来和11合并成一个结点,也可以把20和22合并成一个结点。

第一种方法;14取下来和11合并成一个结点

     ->

第二种方法:把20和22合并成一个结点

                                         

2)如果删除的关键字不在终端结点上(最底层非叶子结点):需要先转换成在终端结点上,再按照在终端结点上的情况来分别考虑对应的方法。
相邻关键字:对于不在终端结点上的关键字它的相邻关键字是其左子树中值最大的关键字或者右子树中值最小的关键字。


第一种情况:存在关键字数量大于「m/2]-1结点的左子树或者右子树,在对应子树上找到该关键字的相邻关键字,然后将相邻关键字替换待删除的关键字。

删除结点10

第①步:找出这个待删除关键字的相邻关键字,比如说下图中10的相邻关键字就是9或者是11.其实就是这个大小序列中该关键字的直接前驱或者是直接后继关键字。

第②步:将这个待删除的关键字和某个相邻关键字互换,然后像删除终端结点一样删除

 

第二种情况:左右子树的关键字数量均等于[m/2]-1,则将这两个左右子树结点合并,然后删除待删除关键字。

删除结点14:

第一步:把14和11进行交换

第二步:像删除终端节点那样删出14

 

B+树:

B+树是常用于数据库和操作系统的文件系统中的一种用于查找的数据结构

m阶的B+树与m阶的B树的主要差异在于:

1)在B+树中,具有n个关键字的结点只含有n棵子树,即每个关键字对应一棵子树;而在B树中,具有n个关键字的结点含有(n+1)棵子树

2)在B+树中,每个结点(非根内部结点)关键字个数n的范围是【m/21】≤n≤m(根结点1≤n≤m),在B树中,每个结点(非根内部结点)关键字个数n的范围是「m/2]-1≤n≤m-1(根结点:1≤n≤m-1)


3)在B+树中,叶结点包含信息,所有非叶结点仅起到索引作用,非叶结点中的每个索引项只含有对应子树的最大关键字和指向该子树的指针,不含有该关键字对应记录的存储地址。


4)在B+树中,叶结点包含了全部关键字,即在非叶结点中出现的关键字也会出现在叶结点中;而在B树中,叶结点包含的关键字和其他结点包含的关键字是不重复的。

 

1)在B+树中,具有n个关键字的结点只含有n棵子树,即每个关键字对应一棵子树;而在B树中,具有n个关键字的结点含有n+1)棵子树

2)在B+树中,每个结点(非根内部结卓)关键字个数n的范围是[m/2]≤n≤m(根结点1≤n≤m),在B树中,每个结点非根内部结点)关键字个数n的范围是]m/2]-1≤n≤m-1(根结点:1≤n≤m-1)

3)在B+树中,叶结点包含信息,所有非叶结点仅起到索引作用,非叶结点中的每个索引项只含有对应子树的最夫关键字和指向该子树的指针,不含有该关键字对应记录的存储地址。而在B树中每个关键字对应一个记录的存储地址。

4)在B+树中,叶结点包含了全部关键字,即在非叶结点中出现的关键字也会出现在叶结点中,而且叶子结点的指针指向记录;而在B树中,叶结点包含的关键字和其他结点包含的关键字是不重复的。

5)在B+树中,有一个指针指向关键字最小的叶子结点,所有叶子结点链接成一个单链表。

 

树与二叉树:https://blog.csdn.net/alzzw/article/details/97283324

线索二叉树:https://blog.csdn.net/alzzw/article/details/97423394
哈夫曼树:https://blog.csdn.net/alzzw/article/details/97809047

二叉搜索(排序)树;https://blog.csdn.net/alzzw/article/details/97563011
平衡二叉树:https://blog.csdn.net/alzzw/article/details/97613193

红黑树:https://blog.csdn.net/alzzw/article/details/97770753

 

应用:

B树和B+广泛应用于文件存储系统以及数据库系统中,在讲解应用之前,我们看一下常见的存储结构:

File System

我们计算机的主存基本都是随机访问存储器(Random-Access Memory,RAM),他分为两类:静态随机访问存储器(SRAM)和动态随机访问存储器(DRAM)。SRAM比DRAM快,但是也贵的多,一般作为CPU的高速缓存,DRAM通常作为内存。这类存储器他们的结构和存储原理比较复杂,基本是使用电信号来保存信息的,不存在机器操作,所以访问速度非常快,具体的访问原理可以查看CSAPP,另外,他们是易失的,即如果断电,保存DRAM和SRAM保存的信息就会丢失。

我们使用的更多的是使用磁盘,磁盘能够保存大量的数据,从GB一直到TB级,但是 他的读取速度比较慢,因为涉及到机器操作,读取速度为毫秒级,从DRAM读速度比从磁盘度快10万倍,从SRAM读速度比从磁盘读快100万倍。下面来看下磁盘的结构:

Disk geometry

如上图,磁盘由盘片构成,每个盘片有两面,又称为盘面(Surface),这些盘面覆盖有磁性材料。盘片中央有一个可以旋转的主轴(spindle),他使得盘片以固定的旋转速率旋转,通常是5400转每分钟(Revolution Per Minute,RPM)或者是7200RPM。磁盘包含一个多多个这样的盘片并封装在一个密封的容器内。上图左,展示了一个典型的磁盘表面结构。每个表面是由一组成为磁道(track)的同心圆组成的,每个磁道被划分为了一组扇区(sector).每个扇区包含相等数量的数据位,通常是(512)子节。扇区之间由一些间隔(gap)隔开,不存储数据。

以上是磁盘的物理结构,现在来看下磁盘的读写操作:

Disk dynamic

如上图,磁盘用读/写头来读写存储在磁性表面的位,而读写头连接到一个传动臂的一端。通过沿着半径轴前后移动传动臂,驱动器可以将读写头定位到任何磁道上,这称之为寻道操作。一旦定位到磁道后,盘片转动,磁道上的每个位经过磁头时,读写磁头就可以感知到位的值,也可以修改值。对磁盘的访问时间分为 寻道时间旋转时间,以及传送时间

由于存储介质的特性,磁盘本身存取就比主存慢很多,再加上机械运动耗费,因此为了提高效率,要尽量减少磁盘I/O,减少读写操作。为了达到这个目的,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。这样做的理论依据是计算机科学中著名的局部性原理:

当一个数据被用到时,其附近的数据也通常会马上被使用。

程序运行期间所需要的数据通常比较集中。

由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率。

预读的长度一般为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页得大小通常为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,然后异常返回,程序继续运行。

文件系统及数据库系统的设计者利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入。为了达到这个目的,在实际实现B-Tree还需要使用如下技巧:

每次新建一个节点的同时,直接申请一个页的空间( 512或者1024),这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了一个node只需一次I/O。如,将B树的度M设置为1024,这样在前面的例子中,600亿个元素中只需要小于4次查找即可定位到某一存储位置。

同时在B+树中,内节点只存储导航用到的key,并不存储具体值,这样内节点个数较少,能够全部读取到主存中,外接点存储key及值,并且顺序排列,具有良好的空间局部性。所以B及B+树比较适合与文件系统的数据结构。下面是一颗B树,用来进行内容存储。

build a large B tree

另外B/B+树也经常用做数据库的索引,这方面推荐您直接看张洋的MySQL索引背后的数据结构及算法原理 这篇文章,这篇文章对MySQL中的如何使用B+树进行索引有比较详细的介绍,推荐阅读。

 

B树代码戳这里:https://blog.csdn.net/alzzw/article/details/97663352

 

本文应用部分转载于: https://www.cnblogs.com/yangecnu/p/Introduce-B-Tree-and-B-Plus-Tree.html

  • 17
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿尔兹

如果觉得有用就推荐给你的朋友吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值