B树系列文章

1. B树-介绍

2. B树-查找

3. B树-插入

4. B树-删除

插入

根据B树的以下两个特性

  • 每一个结点最多有m个子结点
  • 有k个子结点的非叶子结点拥有 k − 1 个键

可以得出,B树每个结点存放键的数量是有上限的是m-1,因此插入操作可能导致结点“溢出”。

插入操作的重点和难点在于结点“溢出”后的再平衡操作

假设有一棵3阶B树,如下图所示。

 

通过给这棵3阶B树插入键值53,来分析B树插入键值的过程。

首先,参考查找的步骤,最终定位到53应该插入到Node21结点的最后

但是这是一个3阶B树,每个结点拥有键的最大数量为2,因此插入53会导致Node21"溢出"

由于Node21结点“溢出”,需要对Node21进行拆分,拆分方法如下

  • 从该结点的原有键和新的键中选择出中位数,这个中位数作为分隔值
  • 小于这一中位数的键放入左边结点,大于这一中位数的键放入右边结点,中位数作为分隔值。
  • 分隔值被插入到父结点中,这可能会造成父结点分裂,分裂父结点时可能又会使它的父结点分裂,以此类推。如果没有父结点(这一结点是根结点),就创建一个新的根结点(增加了树的高度)。

下图是对Node21进行拆分的结果,Node21分裂成两个新的结点和分隔值53,53需要插入父结点中

将分隔值插入父结点中,形成Node2结点,原来的Node21结点被拆分为新的Node21和Node22结点

可以看出来,此时Node2结点键的数量达到了阶数,即达到“溢出”条件了

因此,需要继续对Node2进行拆分

对Node2进行拆分,中间值55提升到root结点,原来的Node2结点拆分成Node2和Node3两个新结点

同样的,此时root结点也“溢出”了,需要对root结点进行拆分

对root结点的拆分,最终造成B树高度的增加

对根结点进行拆分,55被提升,需要创建新的根结点存放键55,键值40和70分别构成新的两个儿子结点,分别是Node1和Node12

此时B数重新平衡了。

这里总结下

所有的插入都从根结点开始。要插入一个新的键,首先搜索这棵树找到新键应该被添加到的对应结点。将新键插入到这一结点中的步骤如下:

如果结点拥有的键数量小于最大值,那么有空间容纳新的键。将新键插入到这一结点,且保持结点中键有序。

否则的话这一结点已经满了,将它平均地拆分成两个结点:

从该结点的原有键和新的键中选择出中位数

小于这一中位数的键放入左边结点,大于这一中位数的键放入右边结点,中位数作为分隔值。

分隔值被插入到父结点中,这可能会造成父结点分裂,分裂父结点时可能又会使它的父结点分裂,以此类推。如果没有父结点(这一结点是根结点),就创建一个新的根结点(增加了树的高度)。

这里是插入的代码

/**
* 插入key
* 时间复杂度为O(logn)
**/
func (bTreeNode *BTreeNode) intert(key int, m int) ([]*BTreeNode, int) {
// 找到第一个不比key小的,注意leaf的数量比key数量多1
idx := 0
for idx < bTreeNode.keyNum && key > bTreeNode.keyList[idx] {
idx++
} // BTreeNode已有该key
if idx < bTreeNode.keyNum && bTreeNode.keyList[idx] == key {
return nil, 0
} if bTreeNode.isLeaf { // 叶子结点
// idx及idx后面元素往后移动
if idx < m-1 {
copy(bTreeNode.keyList[idx+1:], bTreeNode.keyList[idx:])
}
bTreeNode.keyList[idx] = key
bTreeNode.keyNum += 1
} else { // 非叶子结点
// 判断结点是否要满了
// 先往上提
children, midKey := bTreeNode.leafList[idx].intert(key, m)
if children != nil {
// idx是midKey的插入位置,idx后面元素往后移动
if idx < m-1 {
copy(bTreeNode.keyList[idx+1:], bTreeNode.keyList[idx:])
copy(bTreeNode.leafList[idx+2:], bTreeNode.leafList[idx+1:])
}
bTreeNode.keyList[idx] = midKey
bTreeNode.leafList[idx] = children[0]
bTreeNode.leafList[idx+1] = children[1]
bTreeNode.keyNum += 1
}
} if bTreeNode.keyNum == m { // 节点key数量超了
// 从中间将该结点一分为2
mid := bTreeNode.keyNum / 2
midKey := bTreeNode.keyList[mid] // 左儿子
leftNode := createNode(m, mid, bTreeNode.isLeaf)
copy(leftNode.keyList, bTreeNode.keyList[:mid])
copy(leftNode.leafList, bTreeNode.leafList[:mid+1]) // 右边儿子
rightNode := createNode(m, m-mid-1, bTreeNode.isLeaf)
copy(rightNode.keyList, bTreeNode.keyList[mid+1:])
copy(rightNode.leafList, bTreeNode.leafList[mid+1:]) return []*BTreeNode{leftNode, rightNode}, midKey
}
return nil, 0
} /** 插入key
* 时间复杂度O(logn)
**/
func (bTree *BTree) Insert(key int) {
children, midKey := bTree.root.intert(key, bTree.m)
if children != nil {
root := createNode(bTree.m, 1, false)
root.keyList[0] = midKey
root.leafList[0] = children[0]
root.leafList[1] = children[1]
bTree.root = root
}
}
 

B树-插入的更多相关文章

  1. AVL树插入和删除

    一.AVL树简介 AVL树是一种平衡的二叉查找树. 平衡二叉树(AVL 树)是一棵空树,或者是具有下列性质的二叉排序树:    1它的左子树和右子树都是平衡二叉树,    2且左子树和右子树高度之差的 ...

  2. B树——插入和删除

    B树--插入和删除 B树的插入 5阶B数--结点关键字个数向上取整m/2-1≤n≤m-1 即2≤n≤4 连续插入5个元素后,超出来了. 在插入key后,若导致原结点关键字数超过上限,则从中间位置(m/ ...

  3. AVL树插入操作实现

    为了提高二插排序树的性能,规定树中的每个节点的左子树和右子树高度差的绝对值不能大于1.为了满足上面的要求需要在插入完成后对树进行调整.下面介绍各个调整方式. 右单旋转 如下图所示,节点A的平衡因子(左 ...

  4. AVL树插入(Python实现)

    建立AVL树 class AVLNode(object): def __init__(self,data): self.data = data self.lchild = None self.rchi ...

  5. HDU 5687 字典树插入查找删除

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=5687 2016百度之星资格赛C题,直接套用字典树,顺便巩固了一下自己对字典树的理解 #include< ...

  6. Trie树的创建、插入、查询的实现

    原文:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=28977986&id=3807947 1.什么是Trie树 Tr ...

  7. B+树的插入、删除(附源代码)

    B+ Tree Index B+树的插入 B+树的删除 完整测试代码 Basic B+树和B树类似(有关B树:http://www.cnblogs.com/YuNanlong/p/6354029.ht ...

  8. AVL树的插入与删除

    AVL 树要在插入和删除结点后保持平衡,旋转操作必不可少.关键是理解什么时候应该左旋.右旋和双旋.在Youtube上看到一位老师的视频对这个概念讲解得非常清楚,再结合算法书和网络的博文,记录如下. 1 ...

  9. [BZOJ3065]带插入区间K小值 解题报告 替罪羊树+值域线段树

    刚了一天的题终于切掉了,数据结构题的代码真**难调,这是我做过的第一道树套树题,做完后感觉对树套树都有阴影了......下面写一下做题记录. Portal Gun:[BZOJ3065]带插入区间k小值 ...

随机推荐

  1. SpringBoot 整合文件上传 elment Ui 上传组件

    SpringBoot 整合文件上传 elment Ui 上传组件 本文章记录 自己学习使用 侵权必删! 前端代码 博主最近在学 elment Ui 所以 前端使用 elmentUi 的 upload ...

  2. Centos8安装NextCloud记录

    今天在网上学习了这个Nextcloud 网盘的搭建,被折磨的快崩溃了.始终是找不到答案,我在网上查了2天的资料 还是没有找到答案,今天这里总结一下安装的下面的总结: 原文出处在官网:CentOS 8 ...

  3. CF1042E Vasya and Magic Matrix 题解

    题目链接 思路分析 看到题目中 \(n,m \leq 1000\) ,故直接考虑 \(O(n^2)\) 级别做法. 我们先把所有的点按照 \(val\) 值从小到大排序,这样的话二维问题变成序列问题. ...

  4. Tapdata Cloud 2.1.2 来啦:大波细节已就绪!字段类型可批量修改、支持微信扫码登录、新增支持 Vika 为目标

    Tapdata Cloud cloud.tapdata.net 让数据实时可用 Tapdata Cloud 是国内首家异构数据库实时同步云平台,目前支持 Oracle.MySQL.PG.SQL Ser ...

  5. ajax传递参数与controller接收参数映射关系

    将ajax的参数传递至后台controller时,data 中的参数名要与controller中的形参保持一致. 前端ajax代码: 1 $.ajax({ 2 url:"/doLogin&q ...

  6. java-Stream的总结

    JAVA中的Stream 01.什么是Stream Stream是JDK8中引入,Stream是一个来自数据源的元素序列并支持聚合操作.可以让你以一种声明的方式处理数据,Stream 使用一种类似用 ...

  7. 自己写雪花算法IdWorker

    package com.aiyusheng.shopping.util; import java.lang.management.ManagementFactory; import java.net. ...

  8. Java学习day09—-封装和继承

    封装 1.封装 1.1 封装的概念和实现 封装: 属性私有:将属性使用private修饰,表示此属性只能在本类中访问,不让外部直接访问 方法公开:针对每一个属性编写一对公开的方法 get set 分别 ...

  9. 浅谈spring-createBean

    目录 找到BeanClass并且加载类 实例化前 实例化 Supplier创建对象 工厂方法创建对象 方法一 方法二 推断构造方法 BeanDefionition 的后置处理 实例化后 属性填充 sp ...

  10. WebWorker:工作者线程初探

    WebWorker:工作者线程初探 参考资料: 1.Web Worker 使用教程 - 阮一峰:http://www.ruanyifeng.com/blog/2018/07/web-worker.ht ...