高阶数据结构--B树B+树实现原理B树模拟实现--Java

目录

一、B-树概念

二、B-树插入分析

1.用序列{53, 139, 75, 49, 145, 36, 101}构建B树的过程如下:

2.插入过程总结

三、B树插入实现

四、B+树

1.B+树概念

2.B+树的特性

 五、B+树应用

1.索引

 2.Mysql索引

3.InnoDB


一、B-树概念

1970 年, R.Bayer E.mccreight 提出了一种适合外查找的树,它是一种平衡的多叉树,称为 B ( 有些地方写的是 B- 树,注意不要误读成"B 减树 ") 一棵 M (M>2) B 树,是一棵平衡的 M 路平衡搜索树,可以是空树 或者满足一下性质:
1. 根节点至少有两个孩子
2. 每个非根节点至少有 M/2-1( 上取整 ) 个关键字 , 至多有 M-1 个关键字,并且以升序排列
例如:当 M=3 的时候,至少有 3/2=1.5 ,向上取整等于 2 2-1=1 个关键字,最多是 2 个关键字
3. 每个非根节点至少有 M/2( 上取整 ) 个孩子 , 至多有 M 个孩子
例如:当 M=3 的时候,至少有 3/2=1.5 ,向上取整等于 2 个孩子。最多有 3 个孩子。
4. key[i] key[i+1] 之间的孩子节点的值介于 key[i] key[i+1] 之间
5. 所有的叶子节点都在同一层

二、B-树插入分析

为了简单起见,假设 M = 3. 三叉树,每个节点中存储两个数据,两个数据可以将区间分割成三个部分,因此节点 应该有三个孩子 ,为了后续实现简单期间,节点的结构如下:
注意:孩子永远比数据多一个。
插入过程当中,有可能需要分裂,分裂的前提是:
假设,当前是要组成一个M路查找树,关键字数必须<=M-1(这里关键字数>M-1就要进行节点拆分) 规则是:把中间的元素,提取出来,放到父亲节点上,左边的单独构成一个节点,右边的单独构成一个节点。

1.用序列{53, 139, 75, 49, 145, 36, 101}构建B树的过程如下

2.插入过程总结

1. 如果树为空,直接插入新节点中,该节点为树的根节点
2. 树非空,找待插入元素在树中的插入位置(注意:找到的插入节点位置一定在叶子节点中)
3. 检测是否找到插入位置(假设树中的key唯一,即该元素已经存在时则不插入)
4. 按照插入排序的思想将该元素插入到找到的节点中
5. 检测该节点是否满足B-树的性质:即该节点中的元素个数是否等于M,如果小于则满足
6. 如果插入后节点不满足B树的性质,需要对该节点进行分裂:
(1申请新节点
(2找到该节点的中间位置
(3将该节点中间位置右侧的元素以及其孩子搬移到新节点中
(4将中间位置元素以及新节点往该节点的双亲节点中插入,即继续4
7. 如果向上已经分裂到根节点的位置,插入结束

三、B树插入实现

public class MyBTree {
    public static final int M=3;//三叉树
    static class BTreeNode {
        public int[] keys;//关键字
        public BTreeNode[] subs;//孩子节点
        public BTreeNode parent;//父节点
        public int UsedSize;//存储的关键字数量
        public BTreeNode() {
            //这里多给一个空间是为了分裂实现更容易
            keys=new int[M];
            subs=new BTreeNode[M+1];
        }
    }
    public BTreeNode root;

    /**
     * 插入一个元素
     * @param val
     */
    public boolean insert(int val) {
        //B树为空的时候
        if(root==null) {
            root=new BTreeNode();
            root.keys[0]=val;
            root.UsedSize=1;
            return true;
        }
        //当B树不为空的时候
        Pair<BTreeNode,Integer> pair=Find(val);
        if(pair.getVal()!=-1) {
            return false;
        }
        BTreeNode parent=pair.getKey();
        int index=parent.UsedSize-1;
        for(;index>=0;index--) {
            if(parent.keys[index]>=val) {
                parent.keys[index+1]=parent.keys[index];
            }else {
                break;
            }
        }
        parent.keys[index+1]=val;
        parent.UsedSize++;
        if(parent.UsedSize>=M) {
            split(parent);
            return true;
        }else {
            return true;
        }
    }

    /**
     * 分裂节点
     * @param cur
     */
    private void split(BTreeNode cur) {
        BTreeNode parent=cur.parent;
        BTreeNode newNode=new BTreeNode();
        int mid= cur.UsedSize>>1;
        int i=mid+1;
        int j=0;
        while (i<cur.UsedSize) {
            newNode.keys[j]=cur.keys[i];
            newNode.subs[j]=cur.subs[i];
            if(newNode.subs[j]!=null) {
                newNode.subs[j].parent=newNode;
            }
            i++;
            j++;
        }
        newNode.subs[j]=cur.subs[i];
        if(newNode.subs[j]!=null) {
            newNode.subs[j].parent=newNode;
        }
        newNode.UsedSize=j;
        cur.UsedSize=cur.UsedSize-j-1;
        if(cur==root) {
            root=new BTreeNode();
            root.keys[0]=cur.keys[mid];
            root.subs[0]=cur;
            root.subs[1]=newNode;
            root.UsedSize=1;
            cur.parent=root;
            newNode.parent=root;
            return;
        }
        newNode.parent=parent;

        int endT=parent.UsedSize-1;
        for (;endT>=0;endT--) {
            if(parent.keys[endT]>=cur.keys[mid]) {
                parent.keys[endT+1]=parent.keys[endT];
                parent.subs[endT+2]=parent.subs[endT+1];
            }else {
                break;
            }
        }
        parent.keys[endT+1]=cur.keys[mid];
        //将当前父亲节点的孩子节点更改为newNode
        parent.subs[endT+2]=newNode;
        parent.UsedSize++;
        if(parent.UsedSize>=M) {
            split(parent);
        }
    }

    /**
     * 查找B树中是否存在该元素
     * @param val
     * @return
     */
    private Pair<BTreeNode, Integer> Find(int val) {
        BTreeNode cur=root;
        BTreeNode parent = null;
        while (cur!=null) {
            int i=0;
            while (i<cur.UsedSize) {
                if(cur.keys[i]==val) {
                    return new Pair<>(cur,i);
                } else if (cur.keys[i]<val) {
                    i++;
                }else {
                    break;
                }
            }
            parent=cur;
            cur=cur.subs[i];
        }
        return new Pair<>(parent,-1);
    }

    /**
     * 验证B树,如果输出的是一个有序的结果则证明是B树
     * @param root
     */
    private void inorder(BTreeNode root){
        if(root == null)
            return;
        for(int i = 0; i < root.UsedSize; ++i){
            inorder(root.subs[i]);
            System.out.println(root.keys[i]);
        }
        inorder(root.subs[root.UsedSize]);
    }
}

B树验证

public static void main(String[] args) {
        MyBTree bTree=new MyBTree();
        int[] arrays={75,49,36,53,101,139,145};
        for (int i = 0; i < arrays.length; i++) {
            bTree.insert(arrays[i]);
        }
        bTree.inorder(bTree.root);
    }

输出结果 :

36
49
53
75
101
139
145

四、B+树

1.B+树概念

B+树是B-树的变形,也是一种多路搜索树:
1. 其定义基本与B-树相同,除了:
2. 非叶子节点的子树指针与关键字个数相同
3. 非叶子节点的子树指针p[i],指向关键字值属于【k[i]k[i+1])的子树
4. 为所有叶子节点增加一个链指针
5. 所有关键字都在叶子节点出现
B+树的搜索与B-树基本相同,区别是B+树只有达到叶子节点才能命中(B-树可以在非叶子节点中命中),其性能也等 价与在关键字全集做一次二分查找。

 

2.B+树的特性

1. 所有关键字都出现在叶子节点的链表中(稠密索引),且链表中的节点都是有序的。

2. 不可能在非叶子节点中命中。

3. 非叶子节点相当于是叶子节点的索引(稀疏索引),叶子节点相当于是存储数据的数据层。
4. 更适合文件索引系统

 

 五、B+树应用

1.索引

B+树最常见的应用就是用来做索引。索引通俗的说就是为了方便用户快速找到所寻之物,比如:书籍目录可以让读 者快速找到相关信息,hao123网页导航网站,为了让用户能够快速的找到有价值的分类网站,本质上就是互联网 页面中的索引结构。
MySQL官方对索引的定义为:索引(index)是帮助MySQL高效获取数据的数据结构,简单来说:索引就是数据结构。 当数据量很大时,为了能够方便管理数据,提高数据查询的效率,一般都会选择将数据保存到数据库,因此数据库 不仅仅是帮助用户管理数据,而且数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引 用数据,这样就可以在这些数据结构上实现高级查找算法,该数据结构就是索引。

 2.Mysql索引

MyISAM引擎是MySQL5.5.8版本之前默认的存储引擎,不支持事物,支持全文检索,使用B+Tree作为索引结构, 叶节点的data域存放的是数据记录的地址,其结构如下:
上图是以以 Col1 为主键, MyISAM 的示意图,可以看出 MyISAM 的索引文件仅仅保存数据记录的地址 MyISAM 中,主索引和辅助索引( Secondary key )在结构上没有任何区别,只是主索引要求 key 是唯一的,而辅助索引的 key 可以重复 。如果想在 Col2 上建立一个辅助索引,则此索引的结构如下图所示:
同样也是一棵 B+Tree data 域保存数据记录的地址。因此, MyISAM 中索引检索的算法为首先按照 B+Tree 搜索算 法搜索索引,如果指定的Key 存在,则取出其 data 域的值,然后以 data 域的值为地址,读取相应数据记录。 MyISAM的索引方式也叫做 非聚集索引“。

3.InnoDB

InnoDB 存储引擎支持事务 ,其设计目标主要面向在线事务处理的应用,从 MySQL 数据库 5.5.8 版本开始, InnoDB 存储引擎是默认的存储引擎 InnoDB 支持 B+ 树索引、全文索引、哈希索引。但 InnoDB 使用 B+Tree 作为索引结构 时,具体实现方式却与MyISAM 截然不同。
第一个区别是 InnoDB 的数据文件本身就是索引文件 MyISAM 索引文件和数据文件是分离的,索引文件仅保存数 据记录的地址 。而 InnoDB 索引,表数据文件本身就是按 B+Tree 组织的一个索引结构,这棵树的叶节点 data 域保 存了完整的数据记录 。这个索引的 key 是数据表的主键,因此 InnoDB 表数据文件本身就是主索引。

上图是 InnoDB 主索引 (同时也是数据文件)的示意图,可以看到 叶节点包含了完整的数据记录,这种索引叫做聚 集索引 。因为 InnoDB 的数据文件本身要按主键聚集,所以 InnoDB 要求表必须有主键 MyISAM 可以没有), 如果 没有显式指定,则 MySQL 系统会自动选择一个可以唯一标识数据记录的列作为主键 如果不存在这种列,则 MySQL 自动为 InnoDB 表生成一个隐含字段作为主键,这个字段长度为 6 个字节,类型为长整形

第二个区别是 InnoDB 的辅助索引 data 域存储相应记录主键的值而不是地址 , 所有辅助索引都引用主键作为data域。
聚集索引这种实现方式使得按主键的搜索十分高效 ,但是 辅助索引搜索需要检索两遍索引:首先检索辅助索引获得 主键,然后用主键到主索引中检索获得记录。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/935289.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

优选算法——分治(快排)

1. 颜色分类 题目链接&#xff1a;75. 颜色分类 - 力扣&#xff08;LeetCode&#xff09; 题目展示&#xff1a; 题目分析&#xff1a;本题其实就要将数组最终分成3块儿&#xff0c;这也是后面快排的优化思路&#xff0c;具体大家来看下图。 这里我们上来先定义了3个指针&…

Edge SCDN的独特优势有哪些?

强大的边缘计算能力 Edge SCDN&#xff08;边缘安全加速&#xff09;是酷盾安全推出的边缘集分布式 DDoS 防护、CC 防护、WAF 防护、BOT 行为分析为一体的安全加速解决方案。通过边缘缓存技术&#xff0c;智能调度使用户就近获取所需内容&#xff0c;为用户提供稳定快速的访问…

「Mac玩转仓颉内测版45」小学奥数篇8 - 排列组合计算

本篇将通过 Python 和 Cangjie 双语讲解如何计算排列与组合。这道题目旨在让学生学会使用排列组合公式解决实际问题&#xff0c;并加深对数学知识和编程逻辑的理解。 关键词 小学奥数Python Cangjie排列与组合 一、题目描述 编写一个程序&#xff0c;计算从 n 个不同元素中取…

基于Q-Learning的机器人栅格地图路径规划,可以更改地图大小及起始点,可以自定义障碍物,MATLAB代码

基于Q-learning算法的栅格地图路径规划是一种利用强化学习技术来解决路径规划问题的方法。 状态空间定义&#xff1a;在路径规划任务中&#xff0c;状态通常代表机器人或智能体在环境中的位置。状态空间可以是离散的&#xff0c;如网格地图上的特定位置。 动作空间定义&#x…

中电金信携手中远海科,共启贸易金融数智新篇章

在数智化转型成为驱动经济社会高质量发展的新引擎背景下&#xff0c;“数智方案”栏目聚焦金融等国计民生重点行业场景&#xff0c;依托中电金信“源启筑基咨询引领应用重构”的产品及服务体系&#xff0c;输出市场洞察和行业解决方案、应用案例&#xff0c;旨在全面推动行业IT…

抗DDOS设备

0x00 定义: 抗DDOS设备顾名思义&#xff0c;就是防御DDoS攻击的设备&#xff0c;通常包含三个部分&#xff1a;检测中心、清洗中心和管理中心 检测中心主要负责对流量进行检测&#xff0c;发现流量异常后上报管理中心&#xff0c;由管理中心下发引流策略至清洗中心&#xff0…

游戏引擎学习第42天

仓库: https://gitee.com/mrxiao_com/2d_game 简介 目前我们正在研究的内容是如何构建一个基本的游戏引擎。我们将深入了解游戏开发的每一个环节&#xff0c;从最基础的技术实现到高级的游戏编程。 角色移动代码 我们主要讨论的是角色的移动代码。我一直希望能够使用一些基…

Node一、fs 模块、path 模块、端口号、 http 模块、

一、Node.js了解 Node.js是一个跨平台JavaScript运行环境&#xff0c;使开发者可以搭建服务器端的JavaScript应用程序。 概念&#xff1a;使用 Node.js 编写后端程序 / 支持前端工程化 ✓ 后端程序&#xff1a;提供接口和数据&#xff0c;网页资源等 ✓ 前端工程化 &#x…

游戏引擎学习第44天

仓库: https://gitee.com/mrxiao_com/2d_game 向量数学的重要性 矢量数学非常重要&#xff0c;因为 它在某种程度上类似于将C和C视为高于汇编语言的语言&#xff0c;从而使得我们能够以略高的层次思考问题&#xff0c;同时保留大部分性能好处和直接访问的类型。这种思维方式就…

【算法day13】二叉树:递归与回溯

题目引用 找树左下角的值路径总和从中序与后序遍历构造二叉树 今天就简简单单三道题吧~ 1. 找到树左下角的值 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1: 输入: root [2,1,3] 输出: 1 我们…

MindSearch深度解析实践

1. 课程内容 1.1 MindSearch 简介 MindSearch 是一个开源的 AI 搜索引擎框架&#xff0c;具有与 Perplexity.ai Pro 相同的性能。我们可以轻松部署它来构建自己的专属搜索引擎&#xff0c;可以基于闭源的LLM&#xff08;如GPT、Claude系列&#xff09;&#xff0c;也可以使用…

【数据结构进阶】AVL树深度剖析 + 实现(附源码)

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;数据结构 目录 前言 一、AVL树的概念 二、AVL树底层解析及实现 1. 节点的定义 2. 接口声明 3. AVL树的插入 3.1 更新平衡因子 3.2 旋转&#xff08;重点…

黑马商城微服务复习(6)

MQ高级 1. 消息可靠性2. 发送者的可靠性1. 发送者问题2. 生产者重试机制3. 生产者确认机制4. MQ可靠性5. 消费者的可靠性 3. 延迟消息1. 定义2. 死信交换机 1. 消息可靠性 发送消息时丢失&#xff1a; 生产者发送消息时连接MQ失败生产者发送消息到达MQ后未找到Exchange生产者发…

深度优先搜索(DFS)与回溯法:从全排列到子集问题的决策树与剪枝优化

文章目录 前言&#x1f384;一、全排列✨核心思路✨实现步骤✨代码✨时间和空间复杂度&#x1f381;1. 时间复杂度&#x1f381;2. 空间复杂度 &#x1f384;二、子集✨解法一&#xff1a;逐位置决策法&#x1f381;步骤分析&#x1f381;运行示例&#x1f381;代码 ✨解法二&a…

STM32--中断

中断 中断向量表 定义一段固定的内存&#xff0c;以4字节对齐&#xff0c;存放各个中断服务函数程序的首地址。定义在启动文件中。 中断相关寄存器 内核中断不经过中断使能、除能寄存器。 中断优先级 1、抢占优先级&#xff1a;高高抢占优先级可以打断正在执行的低抢占优先…

AUTOSAR 汽车开放系统架构

AUTOSAR 官网 AUTOMOTIVE OPEN SYSTEM ARCHITECTURE AUTOSAR (AUTomotive Open System ARchitecture) is a global partnership of leading companies in the automotive and software industry to develop and establish the standardized software framework and open E/E …

《计算机视觉:瓶颈之辩与未来之路》

一、计算机视觉的崛起 计算机视觉是使用计算机模仿人类视觉系统的科学&#xff0c;让计算机拥有类似人类提取、处理、理解和分析图像以及图像序列的能力。它是一个多学科交叉的领域&#xff0c;与机器视觉、图像处理、人工智能、机器学习等领域密切相关。 计算机视觉行业可分为…

Vue 集成地图

电子地图应用广泛&#xff1a; 网约车 : 在网约车 场景中实现 准定位 、导航 、司乘同显 &#xff0c;精准计费 智慧物流、生活服务等&#xff0c;本专题课程囊括各类应用场景 学习 电子地图解决方案&#xff0c;满足学员工作学习各类需求。 基础知识 学习 集成 地图之前需…

Docker Compose实战三:轻松部署PHP

通过前面的文章&#xff08;Docker Compose基础语法与MySQL部署&#xff09;&#xff0c;你已经掌握了Docker Compose的基本语法和常用指令&#xff0c;并成功部署了一个MySQL数据库服务器。今天&#xff0c;我们将继续深入探索Docker Compose的强大功能&#xff0c;介绍如何使…

【深度学习】深刻理解“变形金刚”——Transformer

Transformer 是一种用于处理序列数据的深度学习模型架构&#xff0c;最初由 Vaswani 等人在 2017 年的论文《Attention is All You Need》中提出。它彻底改变了自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;成为许多高级任务&#xff08;如机器翻译、文本生成、问答…