B树-插入

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″溢出”

B树-插入

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

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

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

B树-插入

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

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

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

B树-插入

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

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

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

B树-插入

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

此时B数重新平衡了。

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
    }
}

Original: https://www.cnblogs.com/amos01/p/16492334.html
Author: Amos01
Title: B树-插入

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/611204/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Docker常用命令

    镜像:Docker 镜像是用于创建 Docker 容器的模板容器:容器是独立运行的一个或一组应用仓库:用来保存镜像,可以理解为代码控制中的代码仓库 一个仓库中包含多个镜像,以镜像为…

    数据库 2023年6月11日
    079
  • 注解

    注解概述 从 JDK5 开始,Java 增加对 &#x5143;&#x6570;&#x636E;的支持,也就是注解,注解与注释是有一定区别的,可以把注解理解…

    数据库 2023年6月15日
    095
  • IPFS 集群部署

    IPFS 和 IPFS-Cluster 默认的端⼝:IPFS: 4001 – 与其他节点通信端⼝ 5001 – API server 8080 – Gateway server I…

    数据库 2023年6月9日
    080
  • 正则表达式

    正则表达式:REGEXP,REGular EXPression。正则表达式分为两类: Basic REGEXP(基本正则表达式 Extended REGEXP(扩展正则表达式) 元…

    数据库 2023年6月15日
    0138
  • leetcode 437. Path Sum III 路径总和 III(中等)

    一、题目大意 给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始,也…

    数据库 2023年6月16日
    092
  • 2_CSS

    1. 什么是CSS 1.1 什么是CSS Cascading Style Sheet 层叠样式表 是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一…

    数据库 2023年6月11日
    052
  • MySQL 中如何定位 DDL 被阻塞的问题

    经常碰到开发、测试童鞋会问,线下开发、测试环境,执行了一个DDL,发现很久都没有执行完,是不是被阻塞了?要怎么解决? 包括在群里,也经常会碰到类似问题:DDL 被阻塞了,如何找到阻…

    数据库 2023年5月24日
    058
  • Mysql 的Innodb引擎和Myisam数据结构和区别

    先大体看一下MySQL的SQL layer层的一个架构流程: 简要介绍一些关键模块: [En] Give a brief description of some key modul…

    数据库 2023年5月24日
    090
  • Figma 快捷键

    作用 WINDOWS MAC 窗口切换到Home Ctrl + 1 Cmd + 1 窗口切换到打开的第一个文件 Ctrl + 2 Cmd + 2 打开菜单搜索 Ctrl + / C…

    数据库 2023年6月6日
    085
  • SpringBoot下配置文件密码加密

    一、导入配置文件 csharp;gutter:true; com.github.ulisesbocchio jasypt-spring-boot-starter 3.0.4<…

    数据库 2023年6月14日
    071
  • 刘畊宏男孩女孩看过来!运动数据分析挖掘!⛵

    💡 作者:韩信子@ShowMeAI📘数据分析 ◉ 技能提升系列:https://www.showmeai.tech/tutorials/33📘AI 面试题库系列:https://w…

    数据库 2023年6月14日
    066
  • git 烂笔头

    &#x89E6;&#x7C7B;&#x65C1;&#x901A;, &#x4E3E;&#x4E00;&#x53CD;&amp…

    数据库 2023年6月9日
    080
  • Java 多线程共享模型之管程(下)

    共享模型之管程 wait、notify wait、notify 原理 Owner 线程发现条件不满足,调用 wait 方法,即可进入 WaitSet 变为 WAITING 状态 B…

    数据库 2023年6月16日
    0110
  • 爬虫基础_正则表达式_补

    正则表达式 正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个 “规则字符串”,这个 “规则…

    数据库 2023年6月11日
    0137
  • 设计模式之(6)——建造者模式

    定义:建造者模式也称为生成器模式,将一个个简单对象一步步构造成一个复杂的对象,将复杂对象的构建和它的表示分离,使得同样的构建过程有不同的表示; 主要解决:系统中复杂对象的创建过程,…

    数据库 2023年6月14日
    074
  • markdown笔记

    注:笔记旨在记录 1.1 展示一级标题(在标题紧接的下一行加若干个’=’) ======= 1.2 展示二级标题 (在标题紧接的下一行加若干个’…

    数据库 2023年6月16日
    098
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球