B+Tree

B+树和B树的定义类似,但是B+树的非叶子结点只存储着索引,所有的数据信息都存储在叶子结点。

和B树相比

①B+树的特性使得在一个磁盘区域中存放更多的索引值,因此可
让一次IO操作读取到更多的索引进行定位,减少IO读取磁盘的操作
,从而提升效率。
②由于B+树的叶子结点都存储着下一个叶子结点的信息,所以,并不需要遍历整个树,只需找到最左边的孩子即可输出所有信息。
③对于B树,一般要比B+树矮一些,对于一些频率查找往往使用B树,让频率较大的结点离树根近些,在启动的时候,让上面几层直接进入内存,并不会出现磁盘IO。
④B+树的优势是在磁盘,对于内存并没有优势

一个争论

对于B+树的结构,有两种说法:
①孩子数和key的数目相同
②同B树一样,孩子树比key数多一
Ps:我此处使用的是第二种说法

建立B+树的具体过程

需要三个类来定义相关结点

①BPTree(存储B树,及相关信息)

class BPTree{//最大存储关键字public int max;//最小存储关键字public int min;//跟结点(可能为叶子结点。也可能为索引结点)public Object root;}

②IndexNode(索引结点,存储索引)

class IndexNode{//关键字个数public int num;//结点数据public int[] index;//结点孩子(可能为索引,也可能为数据)public Object[] child;//父结点(只可能为索引)public IndexNode parent;}

③LeafNode(叶子结点,存储索引及数据)

class LeafNode{//关键字个数public int num;//索引public int[] index;
//  //结点数据和添加索引同步操作
//  public Data[] data;//父结点public IndexNode parent;//同层下一个叶子结点public LeafNode nextLeaf;}

Ps:我之后的操作只是演示B+树的建立,所以未存储数据

新数据寻找位置并插入树中

/*** 定位并添加数据* @param root 根结点* @return 添加数据的结点*/private LeafNode locateData(Object root,int index) {//判断根结点是索引结点还是叶子结点IndexNode iNode = null;LeafNode lNode = null;if(root instanceof IndexNode) {//索引结点,需定位,然后找下一个结点iNode = (IndexNode)root;//定位int locate = 0;for(; locate<iNode.num; locate++) {if(index < iNode.index[locate]) {break;}}return locateData(iNode.child[locate], index);}else if(root instanceof LeafNode) {//叶子结点,可添加数据lNode = (LeafNode)root;//定位int locate = 0;for(; locate<lNode.num; locate++) {if(index < lNode.index[locate]) {break;}}//移动数据for(int j=lNode.num-1; j>= locate; j--) {lNode.index[j+1] = lNode.index[j];}//添加数据lNode.index[locate] = index;lNode.num++;return lNode;}return null;}

结点的分裂

由于树中存在两种结点,而这两种结点的分裂方式不太相同
①叶子结点分裂:找到中间值index,新建一个叶子结点,将其(包含自己)右边部分迁移到新结点中去,调整叶子结点的下一位结点地址(先将原结点的下一位赋给新结点,再将新结点作为原结点下一位),再将该结点上升到父结点,原结点作为左孩子,新结点作为右孩子。

/*** 分裂叶子结点* @param lNode 叶子结点*/private void spiltLeafNode(LeafNode lNode) {int spiltLocate = lNode.num/2;int valueIndex = lNode.index[spiltLocate];IndexNode parent = lNode.parent;//若根结点是叶子结点,则进行更新if(parent == null) {parent = new IndexNode(this.bpTree.max);this.bpTree.root = parent;System.out.print("\t层数加一");}//新建一个叶子结点,将数据分为两部分LeafNode newlNode = new LeafNode(this.bpTree.max);int insert = 0;for(int i = spiltLocate; i < lNode.num; i++) {newlNode.index[insert] = lNode.index[i];lNode.index[i] = 0;
//          newlNode.data[insert] = lNode.data[i];
//          lNode.data[i] = null;insert++;}//更新结点相关信息lNode.num = spiltLocate;newlNode.num = insert;lNode.parent = parent;newlNode.parent = parent;//连接叶子结点newlNode.nextLeaf = lNode.nextLeaf;lNode.nextLeaf = newlNode;//在父结点中定位索引位置int parlocate = 0;for(; parlocate < parent.num; parlocate++) {if(valueIndex < parent.index[parlocate]) {break;}}//移动数据和孩子for(int i=parent.num-1; i >= parlocate; i--) {parent.index[i+1] = parent.index[i];parent.child[i+2] = parent.child[i+1];}//加入父结点,调整相关结构parent.index[parlocate] = valueIndex;parent.num++;parent.child[parlocate] = lNode;parent.child[parlocate+1] = newlNode;//检测父结点(索引结点)if(parent.num > this.bpTree.max) {System.out.print("\t再次分裂");spiltIndexNode(parent);}}

②索引结点分裂:找到中间值index,新建一个索引结点,将其(不包含自己)右边部分迁移到新结点中(此处不要忘了迁移孩子及更改孩子的父结点),再将该结点上升到父结点,原结点作为左孩子,新结点作为右孩子。

/*** 分裂索引结点* @param iNode 索引结点*/private void spiltIndexNode(IndexNode iNode) {int spiltLocate = iNode.num/2;int valueIndex = iNode.index[spiltLocate];IndexNode parent = iNode.parent;//当分裂到根结点if(parent == null) {parent = new IndexNode(this.bpTree.max);this.bpTree.root = parent;System.out.print("\t层数加一");}//新建一个结点,存储分裂的结点IndexNode newInNode = new IndexNode(this.bpTree.max);//跳过中间结点,将后半部分放入新结点中(调整,索引,孩子,孩子结点的父结点)int insert = 0;for(int i = spiltLocate+1; i < iNode.num; i++) {newInNode.index[insert] = iNode.index[i];iNode.index[i] = 0;newInNode.child[insert] = iNode.child[i];iNode.child[i] = null;//此处孩子节点可能为两种对象,需进行转换if(newInNode.child[insert] instanceof LeafNode) {LeafNode midNode = (LeafNode)newInNode.child[insert];midNode.parent = newInNode;}else {IndexNode midNode = (IndexNode)newInNode.child[insert];midNode.parent = newInNode;}insert++;}//调整最后一个孩子newInNode.child[insert] = iNode.child[iNode.num];if(newInNode.child[insert] instanceof LeafNode) {LeafNode midNode = (LeafNode)newInNode.child[insert];midNode.parent = newInNode;}else {IndexNode midNode = (IndexNode)newInNode.child[insert];midNode.parent = newInNode;}iNode.child[iNode.num] = null;//舍去上升到父结点的结点iNode.index[spiltLocate] = 0;newInNode.num = insert;iNode.num = spiltLocate;newInNode.parent = parent;iNode.parent = parent;//定位父结点中的位置int parLocate = 0;for(; parLocate < parent.num; parLocate++) {if(valueIndex < parent.index[parLocate]) {break;}}//调整父结点索引及孩子for(int i = parent.num-1; i >= parLocate; i--) {parent.index[i+1] = parent.index[i];parent.child[i+2] = parent.child[i+1];}//加入中间数据parent.index[parLocate] = valueIndex;parent.child[parLocate] = iNode;parent.child[parLocate+1] = newInNode;parent.num++;//检测父结点(索引结点)if(parent.num > this.bpTree.max) {System.out.print("\t再次分裂");spiltIndexNode(parent);}}

从文件中读取数据,构建B+树

/*** 构建一棵B+树* @param file 文件名* @param m 树的阶树*/public void CreateBPTree(String file,int m) {//先构建根结点if(this.bpTree == null) {bpTree = new BPTree(m);bpTree.root = new LeafNode(bpTree.max);}//读取数据,加入树BufferedReader bReader = null;try {bReader = new BufferedReader(new FileReader(file));String data = null;while((data=bReader.readLine()) != null) {int index = Integer.parseInt(data);//寻找位置,进行插入LeafNode addNode = this.locateData(this.bpTree.root, index);//判断是否需要进行分裂if(addNode != null && addNode.num > this.bpTree.max) {System.out.print("加入"+index+"时,溢出需要分裂");spiltLeafNode(addNode);System.out.println();}}} catch (Exception e) {e.printStackTrace();} finally {try {if(bReader != null) {bReader.close();}} catch (Exception e2) {e2.printStackTrace();}} }

测试

public static void main(String[] args) {BPlusTree bPlusTree = new BPlusTree();bPlusTree.CreateBPTree("Test/BPlusTree.txt", 4);//遍历叶子结点BPTree bpTree = bPlusTree.getBpTree();IndexNode root = (IndexNode)bpTree.root;Object move = root.child[0];while(move != null) {if( move instanceof IndexNode) {IndexNode iNode = (IndexNode)move;move = iNode.child[0];}else {break;}}LeafNode lNode = (LeafNode)move;while(lNode != null) {for(int i=0; i< lNode.num; i++) {System.out.print(lNode.index[i]+" ");}lNode = lNode.nextLeaf;}}

结果
①4阶

最终结果如图:

②3阶

B+Tree及其创建过程相关推荐

  1. java初始化实例化_Java对象的创建过程:类的初始化与实例化

    一.Java对象创建时机 我们知道,一个对象在可以被使用之前必须要被正确地实例化.在Java代码中,有很多行为可以引起对象的创建,最为直观的一种就是使用new关键字来调用一个类的构造函数显式地创建对象 ...

  2. iOS 的本地化使用和创建过程

    在使用本地化语言之前,来看看本地化语言文件内容的结构(这里我以Chinese为例): "Cancel"="取消"; "OK"="确 ...

  3. python不能创建新变量_Python之变量的创建过程!

    Python之变量的创建过程 一.变量创建过程 首先,当我们定义了一个变量name = 'Kwan'的时候,在内存中其实是做了这样一件事: 程序开辟了一块内存空间,将'Kwan'存储进去,再让变量名n ...

  4. 深入浅出Spring Security(二):FilterChainProxy的创建过程

    上篇回顾 框架的核心是一个过滤器,这个过滤器名字叫springSecurityFilterChain,类型是FilterChainProxy WebSecurity和HttpSecurity都是建造者 ...

  5. OpenStack虚机网卡的创建过程

    OpenStack虚机网卡的创建过程 OpenStack最基本和常用的操作就是启动虚机.虚机启动的过程中涉及很多内容,其中非常重要的一个环节就是创建并绑定虚机的虚拟网卡.虚机的创建和管理是Nova的任 ...

  6. 深入理解WMS(一):Window的创建过程

    8.3 Window的创建过程 View是Android中的视图的呈现方式,但是View不能单独存在,它必须附着在Window这个抽象的概念上面,因此有视图的地方就有Window.Android中可以 ...

  7. Android10.0应用进程创建过程以及Zygote的fork流程-[Android取经之路]

    摘要:点击手机桌面图标,例如微信,它是如何启动的呢,让我们从系统源码级来一起分析. 阅读本文大约需要花费1小时. 文章的内容主要还是从源码进行分析,虽然又臭又长,但是如果想要学习Android系统源码 ...

  8. netty 5 alph1源码分析(服务端创建过程)

    研究了netty的服务端创建过程.至于netty的优势,可以参照网络其他文章.<Netty系列之Netty 服务端创建>是 李林锋撰写的netty源码分析的一篇好文,绝对是技术干货.但抛开 ...

  9. SharpDevelop插件系统创建过程全面分析

    前言 2005年2月,我申报了一个学校组织的大学生SRTP项目,项目的题目是数据结构动画演示系统.当初在做项目之前,我无意中买了一本书,书名为<SharpDevelop软件项目开发全程剖析> ...

最新文章

  1. 1137 - Sin your life sin公式 + 枚举
  2. linux基本命令详解の第一季
  3. 大一java实训报告1500字_社会实践报告1500字
  4. 没有add framework support选项_什么?小型机房没有“线”也能很好的管理机房?
  5. 实体服务与虚拟服务迎来数字化发展新契机 中关村助力首都全球数字经济标杆城市建设
  6. 线性代数:线性系统学习笔记
  7. 云单元架构,如何赋能数字化转型呢?
  8. 如何在一个bat批处理文件中调用另一个bat批处理文件?
  9. Excel如何批量根据身份证号码查询出地址
  10. ECshop新手入门模板制作教程[转载]
  11. java下载文件到本地
  12. 纯前端集成视频会议和聊天室
  13. 需求分析(团队作业3)
  14. 开发一个套crm系统软件需要多少钱
  15. 【BP靶场portswigger-服务端4】操作系统命令注入-5个实验(全)
  16. CentOS7环境安装oracleRAC集群遇到的问题总结二(执行root.sh报错ORA-15018和ORA-15020)
  17. Unity PIC 打包assetBundle报错
  18. 解决UnknownError: Failed to get convolution algorithm. This is probably because cuDNN failed to initia
  19. C++ OpenCV视频操作之图像输出文字
  20. 一款IM即时通讯聊天系统源码,包含app和后台源码

热门文章

  1. 关于索引我能说的那些事儿
  2. 熊猫多模式站群系统 开发日志 第二天
  3. 【JAVA 第五章 】课后习题 Vector类的 初使用
  4. [极客大挑战 2019]Knife [RoarCTF 2019]Easy Calc
  5. npm和angular_如何开发,使用和发布Angular库到NPM
  6. win10饥荒服务器未响应,win10系统玩饥荒联机很卡如何解决[多图]
  7. Logistic Regression逻辑回归的损失函数与梯度下降训练
  8. 视图与URLconf
  9. Django获取请求参数方式
  10. 记一次YY笔试中卡住得知识点