最近学习了算法导轮里B树相关的知识,在此写一篇博客作为总结。

1.引言

B树是为磁盘或其他直接存取的辅助存储设备而设计的一种平衡搜索树。B树类似于红黑树,但它与红黑树最大不同之处在于B树的节点可以拥有很多孩子,因此B树的高度会比红黑树小很多,也因此B树在磁盘I/O方面表现要比红黑树好。(对于磁盘操作最耗时的部分在于磁盘读写,而每次读取一个新的树的节点就必须进行一次磁盘读取,因此节点较大、树高度较小的B树会进行较少的磁盘I/O操作)
 

2.B树的定义

一颗B树的定义如下:
  1. 每个节点x均有如下属性:

    • n表示存储在该节点的关键字个数
    • n个关键字本身key1、key2……keyn以非降序存放,即key1 <= key2 <= …… <= keyn
    • 一个leaf布尔值表示该节点是否为叶节点
  2. 每个内部节点包含了n+1个孩子,叶节点没有孩子
  3. 关键字keyi对存储在各子树中的关键字范围加以分割:即比keyi小的元素在其左子树,比keyi大的元素在其右子树
  4. 每个叶节点具有相同的深度
  5. 每个节点包含的关键字个数有上界与下界。我们定义B树的最小度数为t,则除根节点外的每个节点至少有t-1个关键字,每个节点最多有2t-1个关键字(即每个节点最少有t个孩子,最多有2t个孩子)(当一个节点有2t-1个关键字时,我们称它为满的)。
B树的示意图如下:
上图是一个最小度数为2的B树,因此每个节点拥有1个、2个或3个元素,拥有2个、3个或4个孩子,也被称为2-3-4树。
根节点只有一个元素,因此它拥有两个孩子;两个孩子分别拥有3个和2个元素,因此他们分别拥有4个和3个孩子。
看到DEF叶节点位于关键字C与G的中间,表明了关键字对于存储在各子树中的关键字范围进行了分割,其余同理。
 

3.B树的插入

要讲到树,就不得不提树中关键的插入与删除操作,这里我们先总结B树的插入操作。
当我们往B树中插入一个新的关键字时,由于B树节点的关键字是受到限制的,因此当一个节点关键字数目为2t-1时(该节点是满的),我们就必须进行分裂操作。
 

分裂节点

分裂节点的主要操作为把满节点的中间关键字提升至父节点,把原满节点分裂为中间关键字的两个左右节点
其示意图如下:
对于某个非满的节点x,若其孩子节点x.ci为满的(即孩子节点的关键字数目为2t-1)。则把其孩子节点的中间关键字(S)提升为父节点(x节点)的关键字,并把原孩子节点(x.c节点)分成两个t-1个关键字的节点,分别位于中间关键字(S)的左、右。
还有一种比较特殊的情况就是B树根的分裂:
 
分裂是B树长高的唯一途径,因此分裂是非常重要的。
 

插入

讲完分裂操作在讲插入操作就非常简单了。插入的时候我们通过比较不断地根据关键字的值寻找孩子节点,当发现一个满的节点时便分裂,最后找到对应的叶节点时根据关键字的值插入相应位置即可。
下面是一个插入关键字的例子:
B树的初始状态如图所示,这是一颗最小度数为3的B树,即他的关键字个数为2~5个。
插入关键字B,在根节点由于(B < G)往进入G的左节点,到达叶节点后添加至A与C关键字之间。
插入关键字Q:
  1. 在根节点,由于P < Q 而且 Q < X,进入P与X之间的子节点
  2. 发现该子节点是满的,则进行分裂,把关键字T上升到父节点,原子节点分为RS与UV,分为在T关键字的左右
  3. 由于Q < T,于是进入T的左子节点
  4. 在RS叶节点中找到对应位置并加入
插入关键字L:
  1. 发现根节点是满的,分裂根节点,上移P
  2. L < P 进入P的左子节点
  3. G < L < M,进入G与M关键字间的节点
  4. 在叶节点的相应位置中插入
插入关键字F:
  1. F < P,进入P的左子节点
  2. F < G,进入G的左子节点
  3. 发现满的节点,分裂,上移C
  4. F > C,进入C的右子节点
  5. 在叶节点的相应位置中插入
 

4.B树的删除

讲完了B树的插入操作,我们再来讲讲B树的删除操作。
对于删除操作,我们必须保证每个节点在删除前必须至少有t(最小度数)个关键字。
首先我们把要删除的关键字(假设为k)分两种情况:
  • 关键字k在叶节点中:直接删除
  • 关键字k在内部节点中,分三种情况:
    • k的左子节点拥有t个关键字,则把k的左子节点的最后一个关键字(假设为j)上移到父节点,然后递归的删除j
    • k的右子节点拥有t个关键字,则把k的右子节点的第一个关键字(假设为l)上移到父节点,然后递归的删除l
    • k的左右子节点都只有t-1个关键字,则把k下降与左右子节点合并成一个拥有2t-1个关键字的节点,然后递归的删除k
然后我们再定义一些在寻找删除节点路上的操作:如果在寻找删除节点的路上,我们发现某个节点关键字数只有t-1个关键字,则分两种情况:
  1. 看该节点的相邻兄弟节点是否含有至少t个关键字,如果是则向相邻的兄弟节点“借一个关键字”(一该节点的左节点为例:把左节点的最后一个关键字上升至父节点,然后父节点位置的节点下移到关键字个数为t-1的节点上)
  2. 如果该节点相邻的兄弟节点都只含有t-1个关键字,则选择一个兄弟节点合并,并把两兄弟之间的父节点下移
下面我们来看一个B树删除的例子:
B树的初始状态如图,这是一颗最小度数为3的B树,即每个节点拥有2~5个关键字。
删除F操作:
  1. F < P,进入P的左子节点
  2. C < F < G,进入C与G之间的子节点
  3. 在叶节点中删除F
删除M操作:
  1. M < P,进入P的左子节点
  2. 在内部节点中发现M,查看M的左子节点JKL,拥有3个关键字,则把最后一个关键字L上升至M的位置,递归的删除L
  3. 节点JKL是叶节点,直接删除L即可
删除G操作:
  1. G < P,进入P的左子节点
  2. 在内部节点中发现G,查看G的左右子节点均只有2个关键字(不足最小度数3个),下降G关键字并合并其左右子节点
  3. 节点DEGJK为叶节点,直接删除G即可
删除D操作:
  1. D < P,进入P的左子节点
  2. 发现内部节点CL只有2个关键字(不足最小度数3个),其兄弟节点也只有2个关键字,下降父节点P,与兄弟节点一起合并成一个节点
  3. 由于C < D < L,进入C与L间的子节点
  4. DEJK节点是叶节点,直接删除D即可
删除B操作:
    1. B < C,进入E的左子节点
    2. 发现节点AC只有两个关键字,其兄弟节点EJK有三个关键字,则E上移到父节点,C下移到子节点,变为节点ABC
    3. 节点ABC为叶节点,直接删除B即可

算法导轮之B树的学习的更多相关文章

  1. 从K近邻算法谈到KD树、SIFT+BBF算法

    转自 http://blog.csdn.net/v_july_v/article/details/8203674 ,感谢july的辛勤劳动 前言 前两日,在微博上说:“到今天为止,我至少亏欠了3篇文章 ...

  2. Java数据结构和算法(一)树

    Java数据结构和算法(一)树 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 前面讲到的链表.栈和队列都是一对一的线性结构, ...

  3. python常用算法(5)——树,二叉树与AVL树

    1,树 树是一种非常重要的非线性数据结构,直观的看,它是数据元素(在树中称为节点)按分支关系组织起来的结构,很像自然界中树那样.树结构在客观世界中广泛存在,如人类社会的族谱和各种社会组织机构都可用树形 ...

  4. 萌新笔记——用KMP算法与Trie字典树实现屏蔽敏感词(UTF-8编码)

    前几天写好了字典,又刚好重温了KMP算法,恰逢遇到朋友吐槽最近被和谐的词越来越多了,于是突发奇想,想要自己实现一下敏感词屏蔽. 基本敏感词的屏蔽说起来很简单,只要把字符串中的敏感词替换成"* ...

  5. 数据结构与算法系列研究五——树、二叉树、三叉树、平衡排序二叉树AVL

    树.二叉树.三叉树.平衡排序二叉树AVL 一.树的定义 树是计算机算法最重要的非线性结构.树中每个数据元素至多有一个直接前驱,但可以有多个直接后继.树是一种以分支关系定义的层次结构.    a.树是n ...

  6. 数据结构与算法(九):AVL树详细讲解

    数据结构与算法(一):基础简介 数据结构与算法(二):基于数组的实现ArrayList源码彻底分析 数据结构与算法(三):基于链表的实现LinkedList源码彻底分析 数据结构与算法(四):基于哈希 ...

  7. Java数据结构和算法(二)树的基本操作

    Java数据结构和算法(二)树的基本操作 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 一.树的遍历 二叉树遍历分为:前序遍 ...

  8. 用KMP算法与Trie字典树实现屏蔽敏感词(UTF-8编码)

    前几天写好了字典,又刚好重温了KMP算法,恰逢遇到朋友吐槽最近被和谐的词越来越多了,于是突发奇想,想要自己实现一下敏感词屏蔽. 基本敏感词的屏蔽说起来很简单,只要把字符串中的敏感词替换成“***”就可 ...

  9. 第15个算法-实现 Trie (前缀树)(LeetCode)

    解法代码来源 :https://blog.csdn.net/whdAlive/article/details/81084793 算法来源:力扣(LeetCode)链接:https://leetcode ...

随机推荐

  1. Python内置函数(20)——hex

    英文文档: hex(x) Convert an integer number to a lowercase hexadecimal string prefixed with "0x" ...

  2. OpenShift实战(三):OpenShift持久化存储Redis

    1.模板定义 修改OpenShift自带模板 [root@master1 pv]# oc edit template redis-persistent 添加如下: 2.创建PV 编辑redis pv ...

  3. Spring Security 入门(3-11)Spring Security 的使用-自定义登录验证和回调地址

    配置文件 security-ns.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmln ...

  4. django中图片的上传和显示

    上传图片实际上是 把图片存在服务器的硬盘中,将图片存储的路径存在数据库中. 1 首先要配置文件上传的路径: 1.1 建立静态文件目录 在项目根目录下 新建一个 static文件夹,下面再建立一个med ...

  5. python 开发之路 -MySQL

    阅读目录 第一篇 : 数据库 之 基本概念 第二篇 : MySQL 之 库操作 第三篇 : MySQL 之 表操作 第四篇 : MySQL 之 数据操作 第五篇 : MySQL 之 视图.触发器.存储 ...

  6. oracle中求1到100之间的质数和

    declare i number:=1; j number:=0; sum1 number:=0;begin while(i<100) loop i:=i+1; j:=2; while(mod( ...

  7. spark算子:partitionBy对数据进行分区

    def partitionBy(partitioner: Partitioner): RDD[(K, V)] 该函数根据partitioner函数生成新的ShuffleRDD,将原RDD重新分区. s ...

  8. netcore webapi帮助文档设置

    如何建 .netcore webapi 项目这个就不说了,这个都没有没必要看下去. 我这里是.netcore 2.0,虽然没测过1.0的,但想来差不多. 1.Nuget Packages安装,使用程序 ...

  9. Spring Cloud学习笔记-008

    继承特性 通过上节的示例实践,当使用Spring MVC的注解来绑定服务接口时,几乎完全可以从服务提供方的Controller中依靠复制操作,构建出相应的服务客户端绑定接口.既然存在这么多复制操作,自 ...

  10. python3.6安装PyQt5

    1.安装环境: python3.6 win8系统 2.安装方法 直接在windows cmd命令行(不需要进入python命令行模式)内输入 pip install PyQt5 等一会就行了. 用pi ...