最近学习了算法导轮里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内置函数(2)——divmod

    英文文档: divmod(a, b) Take two (non complex) numbers as arguments and return a pair of numbers consisti ...

  2. api-gateway实践(09)支持rest服务注册

    一.GET-GET 1.前端定义 2.后端定义 2.1.基础定义 2.2.path参数.head参数.query参数 2.3.常量参数 2.4.系统参数 2.5.结果定义 二.POST-POST 1. ...

  3. Spring Security 入门(3-11)Spring Security 的登录密码验证过程 UsernamePasswordAuthenticationFilter

    认证过程如下 一.先判断请求(请求必须是post请求)地址是否为配置的 login-processing-url 值(默认/j_spring_security_check),如果不是,则放行,进入下一 ...

  4. jsp连接书库DatabaseUtil类

    public class DatabaseUtil { private static String driver = ConfigManager.getProperties("driver& ...

  5. 在删除一个指针之后,一定将该指针设置成空指针(即在delete *p之后一定要加上: p=NULL)

    在删除一个指针之后,一定将该指针设置成空指针(即在delete *p之后一定要加上: p=NULL)

  6. Hive:把一段包含中文的sql脚本粘贴到beeline client运行中文乱码

    背景: 在做项目过程中不可能hive表中都是非中文字段.在最近做的项目中就遇到需要在beeline界面上执行查询脚本,但脚本中包含中文,正常一个脚本用文本写好后,粘贴到beeline窗口运行时,发现中 ...

  7. lvs 负载均衡 NAT模式

    1.原理 基于NAT机制实现.当用户请求到达director之后,director将请求报文的目标地址(即VIP)改成选定的realserver地址,同时将报文的目标端口也改成选定的realserve ...

  8. 开发一个成功APP的六个技巧

    越来越多的人开始使用智能手机,平板电脑或其他的移动设备.出于这个原因,移动APP开发已成为当今软件开发中增长最快的领域之一.本文提供九个简单而有效的提示,可帮助您规划和实施成功的移动APP. 1.目标 ...

  9. Python系列-python函数(functools)

    有一些函数内置到了functools这个模块里 partial(func, *args, **keywords) from functools import partial def add(x,y): ...

  10. [BZOJ 2654]tree(陈立杰)

    Description 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有need条白色边的生成树. 题目保证有解. Input 第一行V,E,need分别表示点数,边数和需要的白色 ...