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. ShardingSphere-proxy-5.0.0建立mysql读写分离的连接(六)

    一.修改配置文件config-sharding.yaml,并重启服务 # # Licensed to the Apache Software Foundation (ASF) under one or ...

  2. java反序列化漏洞专项

    背景条件:java的框架,java的应用程序,使用序列化,反序列化操作非常多.在漏洞挖掘中,代码审计中,安全研究中,反序列化漏洞是个重点项,不可忽视.尤其是java应用程序的rce,10个里面有7个是 ...

  3. Transferable Joint Attribute-Identity Deep Learning for Unsupervised Person Re-Identification理解

    简介:这篇文章属于跨域无监督行人再识别,不同于大部分文章它使用了属性标注.旨在于能够学习到有属性语义与有区分力的身份特征的表达空间(TJ-AIDL),并能够转移到一个没有看到过的域. 贡献: 提出了一 ...

  4. eclipse调用MySQL数据库的方法

    今天来总结一下使用如何使用eclipse调用MySQL数据库的数据. 一.设置eclipse 我们首先来设置一下eclipse. 在下部的Servers中右键选择new,选择server 之后在新弹出 ...

  5. MIT 6.824 Llab2B Raft之日志复制

    书接上文Raft Part A | MIT 6.824 Lab2A Leader Election. 实验准备 实验代码:git://g.csail.mit.edu/6.824-golabs-2021 ...

  6. 前端学做 PPT

    前端学做 PPT 公司做技术分享.年终总结都需要用到ppt. 要快速.省事的做出高质量的 ppt,一方面需要熟练使用制作 ppt 的工具,另一方面得知道用工具做成什么样子才是好作品.前者比较简单,后者 ...

  7. Redis_

    Redis学习 Redis学习(一) 1. NoSQL的引言 NoSQL(Not Only SQL ),意即不仅仅是SQL, 泛指非关系型的数据库.Nosql这个技术门类,早期就有人提出,发展至200 ...

  8. YII 技巧

    大部分来源于  https://getyii.com/topic/47#comment24 获取当前Controller name和action name(在控制器里面使用) echo $this-& ...

  9. 将Nginx配置成系统开机启动服务

    # 如何将nginx配置成我们的系统服务 # 1.在/usr/lib/systemd/system目录下面配置nginx.service内容 # 如果想要详细了解制作的过程:https://blog. ...

  10. devops-1:代码仓库git的使用

    devops-gitlab 介绍 gitlab同github.gitee和bitbucket功能一致,都是提供一个存储代码的服务,这里就以gitlab为例,学习一下如何结合git工具使用. 核心组件: ...