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. Linux下修改RabbitMQ密码

    1,首先查看用户列表 rabbitmqctl list_users 2,修改对应用户密码 其中username 为用户名, newpasswd为新密码 rabbitmqctl change_passw ...

  2. C#生成putty格式的ppk文件(支持passphrase)

    背景 2022国家级护网行动即将开启,根据阿里云给出的安全建议,需要将登陆Linux的方式改为密钥对方式.我这里使用的远程工具是自己开发的,能够同时管理Windows和Linux,但是以前不支持密钥对 ...

  3. Python调用Outlook发邮件

    调用Outlook发送邮件 需安装pypiwin32模块:pip install pypiwin32 1. 发送普通邮件 import win32com.client as win32 outlook ...

  4. zookeeper和spring cloud版本冲突

    1.使用elastic-job进行任务调度,而核心的就是使用zookeeper进行管理,但这个与spring cloud 冲突造成启动不了 |ERROR |main |SpringApplicatio ...

  5. idea插件和springboot镜像

    主题 https://blog.csdn.net/zyx1260168395/article/details/102928172 springboot镜像 http://start.springboo ...

  6. Java开发学习(十六)----AOP切入点表达式及五种通知类型解析

    一.AOP切入点表达式 对于AOP中切入点表达式,总共有三个大的方面,分别是语法格式.通配符和书写技巧. 1.1 语法格式 首先我们先要明确两个概念: 切入点:要进行增强的方法 切入点表达式:要进行增 ...

  7. 关于2022年3月9日之后Typora登录不了--已解决

    p.s.今天是2022.7.27,软件版本:13.6.1 (以下所有方法,亲自尝试后整理出的) 报错信息: This beta version of typora is expired, please ...

  8. 解决线程安全问题_同步方法和解决线程安全问题_Lock锁

    解决线程安全问题_同步方法 package com.yang.Test.ThreadStudy; import lombok.SneakyThrows; /** * 卖票案例出现了线程安全的问题 * ...

  9. 从matlab的bwmorph函数的'majority'参数中扩展的一种二值图像边缘光滑的实时算法。

    在matlab的图像处理工具箱中,有一系列关于Binary Images的处理函数,都是以字母bw开头的,其中以bwmorph函数选项最为丰富,一共有'bothat'.'branchpoints'.' ...

  10. Spring 常见面试题总结 | JavaGuide

    首发于 JavaGuide 在线网站:Spring 常见面试题总结 最近在对 JavaGuide 的内容进行重构完善,同步一下最新更新,希望能够帮助你. Spring 基础 什么是 Spring 框架 ...