1.出现缘由

当数据量很大时,内存不够,找磁盘。为了提高取数据的速度,减少树的高度->多叉

2.特点

  • 每个结点包含多个关键字,包含上界和下界。最小度数(取决于磁盘块)做衡量t>=2
  • 所有的叶子结点都在同一层,并且不带信息 (可以看做是外部结点或查找失败的结 点,实际上这些结点不存在,指向这些结点的指针为空)
  • 对于一个有x个关键字的结点,它有x+1个孩子
  • 每个结点中的关键字升序
  • 根结点至少包含一个关键字,但是上界是一样的

3.插入

主动插入,往叶子结点插

在插入寻找的过程中,只要碰到满结点都做拆分,不管他是不是目标结点

4.删除

4.1如果待删除的结点在结点x中 且x是叶子结点直接删除

4.2如果待删除的结点在结点x中 且x是内部结点。

4.2.1如果位于x中某个关键字前一个孩子y至少有t个关键字

删除关键字G,其前一个孩子为DEF,关键字G的直接前驱为F,删除F,然后将G替换为F

4.2.2如果位于x中某个关键字前一个孩子y少于t个关键字,后一个孩子不少于t个关键字

删除关键字C,其前一个孩子为AB,小于t=3,后一个孩子DEF不小于3,关键字C的直接后继为D,删除D,然后将C替换为D

4.2.3如果位于x中某个关键字前、后孩子都少于t个关键字

删除关键字C,前一个孩子为AB,后一个孩子为DE(此处假设F已经被删掉了),均小于t=3,合并关键字C和结点DE到结点AB中,此时C在叶子结点中,直接删除

4.3如果打破了b树规则(删除是递归过程,删除和调整是一体的)

4.3.1如果子树和他相邻的兄弟都只有t-1,子树和兄弟合并,父结点一个关键字下移

4.3.2如果只有子树是t-1相邻的兄弟至少t个,则将x的一个关键字下移到子树中,将相邻的兄弟中的一个关键字上移到x中,相应的孩子指针也上移,使得x新增加一个关键字(直接返回不需要递归)

package Tree.bTree;import java.util.LinkedList;public class BTreeNode {//b树的阶int M;//关键字列表LinkedList<Integer> values;//父结点BTreeNode parent;//孩子列表LinkedList<BTreeNode> children;//构造构造一棵空的B-树private BTreeNode(){this.values = new LinkedList<Integer>();this.children = new LinkedList<BTreeNode>();}//构造一棵空的m阶B-树public BTreeNode(int m){this();if(m<3){throw new RuntimeException("阶数大于2");}this.M = m;}//根据父结点构造一个空的孩子结点public BTreeNode(BTreeNode parent){this(parent.M);this.parent = parent;}/*** 获取根结点* @return 根结点*/public BTreeNode getRoot(){BTreeNode p = this;while(!p.isRoot()){p = p.parent;}return p;}/*** 判断当前结点是不是根结点* @return 是就返回真*/public boolean isRoot(){return parent == null;}/*** 判断当前结点是不是空结点* @return 是就真*/public boolean isEmpty(){if(values == null||values.size()==0){return true;}return false;}/*** 清空当前结点,保留父关系*/public void clear(){values.clear();children.clear();}/*** 从当前结点往下查找目标值target* @param target 目标值* @return 找到返回找到的结点,没有就返回null的叶子结点*/public BTreeNode search(int target){if(isEmpty()){return this;}int valueIndex = 0;while(valueIndex < values.size()&&values.get(valueIndex)<=target){if(values.get(valueIndex) == target){return this;}valueIndex++;}return children.get(valueIndex).search(target);}/***插入结点,先找到根结点,从根结点往下找到插入的位置,如果是重复的数据就抛出异常* 插入可能会产生新的根结点,返回新的结点* @param e 插入的关键字* @return 新的根结点*/public BTreeNode insert(int e){if(isEmpty()){values.add(e);children.add(new BTreeNode(this));children.add(new BTreeNode(this));return this;}BTreeNode p = getRoot().search(e);if(!p.isEmpty()){throw new RuntimeException("cannot insert duplicate key to B-Tree, key: " + e);}insertNode(p.parent,e,new BTreeNode(p.M));return getRoot();}/*** 向指定结点内插入关键字和关键字右侧的孩子结点,如果没有大于2m-1,就找到结点内部的插入位置,然后直接插入* 一定是叶子插入* @param node 插入的结点* @param e 待插入的关键字* @param eNode 待插入的关键字右侧的孩子结点*/private void insertNode(BTreeNode node,int e,BTreeNode eNode){int valueIndex = 0;while(valueIndex < node.values.size()&&node.values.get(valueIndex)<e){valueIndex++;}node.values.add(valueIndex,e);eNode.parent = node;node.children.add(valueIndex+1,eNode);//这里的M阶相当于2m-1if(node.values.size()>M-1){int upIndex = M/2;int up = node.values.get(upIndex);//当前结点分为左右两部分,左边的parent不变,新建右边结点瓜分原来的右半边BTreeNode rNode = new BTreeNode(M);rNode.values = new LinkedList<>(node.values.subList(upIndex+1,M));rNode.children = new LinkedList<>(node.children.subList(upIndex+1,M+1));for (BTreeNode rChild : rNode.children){rChild.parent = rNode;}node.values = new LinkedList<>(node.values.subList(0,upIndex));node.children = new LinkedList<>(node.children.subList(0,upIndex+1));//从根结点上升,选取上升关键字作为新的根结点if(node.parent == null){node.parent = new BTreeNode(M);node.parent.values.add(up);node.parent.children.add(node);node.parent.children.add(rNode);rNode.parent = node.parent;return;}//父结点增加关键字,递归调用insertNode(node.parent,up,rNode);}}/*** 以当前节点为根,在控制台打印B-树*/public void print() {printNode(this, 0);}/*** 控制台打印节点的递归调用** @param node 要打印节点* @param depth 递归深度*/private void printNode(BTreeNode node, int depth) {StringBuilder sb = new StringBuilder();for(int i = 1; i < depth; i++) {sb.append("|    ");}if(depth > 0) {sb.append("|----");}sb.append(node.values);System.out.println(sb.toString());for(BTreeNode child : node.children) {printNode(child, depth+1);}}public BTreeNode delete(int e){if(isEmpty()){return this;}BTreeNode p = getRoot().search(e);if(p.isEmpty()) {throw new RuntimeException("the key to be deleted is not exist, key: " + e);}//找到目标结点的位置int valueIndex = 0;while(valueIndex < p.values.size() && p.values.get(valueIndex) < e){valueIndex++;}//不是最下层结点中的关键字if(!p.children.get(0).isEmpty()){BTreeNode rMin = p.children.get(valueIndex);while(!rMin.children.get(0).isEmpty()){//一直找到叶子结点rMin = rMin.children.get(0);}//用最小的最大关键字替换p.values.set(valueIndex,rMin.values.get(0));//删除了最下层结点有可能导致关键字数小于t-1//rMin是最下层结点,valueIndex是原结点的位置return delete(rMin,valueIndex,0);}return delete(p,valueIndex,0);}/*** 删除指定结点中的关键字和孩子,并处理删除后打破b-树规则的情况* @param target 目标结点* @param valueIndex 关键字索引* @param childIndex 孩子索引* @return 删除完成后的根结点*/private BTreeNode delete(BTreeNode target,int valueIndex,int childIndex){target.values.remove(valueIndex);target.children.remove(childIndex);//目标结点满足规则直接返回,不进行下一步调整if(target.children.size() >= Math.ceil(M/2.0)){return target.getRoot();}//根结点比较特殊if(target.isRoot()){//孩子数量满足要求if(target.children.size()>1){return target;} else{//只有一个孩子,直接上移,返回BTreeNode newRoot = target.children.get(0);newRoot.parent = null;return newRoot;}}//找到目标结点的位置int parentChildIndex = 0;while(parentChildIndex < target.parent.children.size() && target.parent.children.get(parentChildIndex) != target) {parentChildIndex++;}/*** 等于0表示没有左兄弟* 不满足b树结构要求,下面就是进行调整步骤*/if(parentChildIndex > 0 &&target.parent.children.get(parentChildIndex - 1).values.size() >= Math.ceil(M/2.0)){//左兄弟关键字数大于t-1int downKey = target.parent.values.get(parentChildIndex-1);BTreeNode leftSibling = target.parent.children.get(parentChildIndex-1);int upKey = leftSibling.values.remove(leftSibling.values.size()-1);//删除孩子用来替换的关键字BTreeNode mergeChild = leftSibling.children.remove(leftSibling.children.size()-1);//关键字下移target.values.add(0, downKey);//孩子指针也上移target.children.add(0, mergeChild);//替换target.parent.values.set(parentChildIndex-1, upKey);return target.getRoot();} else if(parentChildIndex < target.parent.children.size()-1 &&target.parent.children.get(parentChildIndex+1).values.size() >= Math.ceil(M/2.0)){// 右兄弟关键字数大于 ceil(M/2)-1,且有右孩子int downKey = target.parent.values.get(parentChildIndex);BTreeNode rightSibling = target.parent.children.get(parentChildIndex+1);int upKey = rightSibling.values.remove(0);BTreeNode mergeChild = rightSibling.children.remove(0);target.values.add(downKey);target.children.add(mergeChild);target.parent.values.set(parentChildIndex, upKey);return target.getRoot();} else{//左右兄弟关键字数都不大于t-1int parentValueIndex;if(parentChildIndex > 0){//如果有左兄弟,和左兄弟合并BTreeNode leftSibling = target.parent.children.get(parentChildIndex -1);//加上父结点的关键字parentValueIndex = parentChildIndex - 1;int downKey = target.parent.values.get(parentValueIndex);leftSibling.values.add(downKey);//加上目标结点的剩余信息leftSibling.values.addAll(target.values);target.children.forEach(c -> c.parent = leftSibling);leftSibling.children.addAll(target.children);} else{//没有左兄弟,和右兄弟合并BTreeNode rightSibling = target.parent.children.get(parentChildIndex + 1);//加上父结点关键字parentValueIndex = parentChildIndex;int downKey = target.parent.values.get(parentValueIndex);rightSibling.values.add(0,downKey);//加上目标结点的剩余信息rightSibling.values.addAll(0, target.values);target.children.forEach(c -> c.parent = rightSibling);rightSibling.children.addAll(0,target.children);}//递归删除父结点关键字和孩子return delete(target.parent,parentValueIndex,parentChildIndex);}}
}

B-树(插入删除)代码+逻辑相关推荐

  1. 浅析B树、B+树插入删除操作(附代码实现)

    首先自平衡树是为了解决二叉搜索树在有序数据中退化为链表的问题(即查找时间退化为 O(n) 级别). 自平衡树中,B树.B+树可以说是最简单的,没有旋转.变色等操作.我们可以拿多路平衡查找树和同样是自平 ...

  2. 【数据结构笔记12】平衡二叉树,AVL树,RR旋转/LL旋转/LR旋转/RL旋转,AVL树插入的代码实现

    本次笔记内容: 4.2.1 什么是平衡二叉树 4.2.2 平衡二叉树的调整 文章目录 什么是平衡二叉树 评价查找长度ASL 平衡因子(Balance Factor,BF) 平衡二叉树(Balanced ...

  3. python Trie树和双数组TRIE树的实现. 拥有3个功能:插入,删除,给前缀智能找到所有能匹配的单词...

    #coding=utf-8 #字典嵌套牛逼,别人写的,这样每一层非常多的东西,搜索就快了,树高26.所以整体搜索一个不关多大的单词表 #还是O(1). ''' Python 字典 setdefault ...

  4. 数据结构-----AVL树的插入删除操作

    对于AVL的插入和删除,主要利用的就是上篇文章所述的四种旋转操作,根据插入后不同的结构选用不同的方式复原平衡. 再次声明一下,http://www.cnblogs.com/QG-whz/p/51672 ...

  5. 二叉树排序树插入、创建、删除和查找

    二叉树排序树插入.创建.删除和查找 二叉排序树的概念 ​ 二叉排序树又称二叉查找树,它或者是一棵空树,或者是具有下列性质的二叉树 若它的左子树不为空,则左子树上所有结点的值均小于根结点的值 若它的右子 ...

  6. c++《AVL树的概念》《AVL树的插入》《AVL树的旋转》《AVL树的验证》《AVL树的删除》《AVL树的性能》

    4.1 AVL树 4.1.1 AVL树的概念 二叉搜索树虽可以缩短查找的效率,**但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当 于在顺序表中搜索元素,效率低下.**因此,两位俄罗斯的 ...

  7. 数据结构---二叉平衡排序树的删除

    数据结构-二叉平衡排序树的删除 原理:参考趣学数据结构 代码: #include<stdio.h> #include<stdlib.h> typedef struct avlT ...

  8. 二叉搜索树-php实现 插入删除查找等操作

    二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的 ...

  9. 树的数据结构代码_如何以无代码方式学习树数据结构

    树的数据结构代码 The tree data structure can form some of the most useful and complex data structures in all ...

  10. Android 天气APP(十四)修复UI显示异常、优化业务代码逻辑、增加详情天气显示

    上一篇:Android 天气APP(十三)仿微信弹窗(右上角加号点击弹窗效果).自定义背景图片.UI优化调整 添加管理城市 新版------------------- 一.添加管理城市页面 二.沉浸式 ...

最新文章

  1. 科研文献|季节变化是流域尺度上土壤抗性变化的主要驱动因素
  2. Hadoop之父Doug Cutting:Lucene到Hadoop的开源之路
  3. java的8中数据类型_java 8种基本数据类型
  4. LoadRunner监控局域网内其他服务器系统资源设置
  5. python目标检测答案_你好,这里有一份2019年目标检测指南
  6. 印度评论九章量子计算机,张礼立:中国 “九章”量子计算机到底厉害在哪?...
  7. yii mysql 操作数据库_YII2中操作数据库的方式
  8. 智慧、智能图书馆管理平台系统+web端业务数据管理平台+Axure通用web端高保真交互业务数据管理平台+铭牌管理+设备监控+系统管理+内容管理+机构列表管理+用户权限管理+专题管理+服务包管理
  9. 程序员VS产品经理日常
  10. android 二级 滚动,android使用 ScrollerView 实现 可上下滚动的分类栏实例
  11. JAVA实现飞机大战详解
  12. LPC_2136 PLC,扩展方案,兼容西门子S7-200 CPU 224XP,兼容西门子软件
  13. 计算机vb题库程序代码编写,11计算机专业VB试题(二)
  14. Hibernate - HQL对象检索详解
  15. 获取当前系统时间(取相对于系统时间的前一周时间)
  16. 中国巧克力生产线及解决方案市场趋势报告、技术动态创新及市场预测
  17. Excel表格公式大全
  18. BOEHM GC原理及总结
  19. 泰安链底层系统设计、核心优势、技术实现
  20. 探究App推广之路:流量思维永不死 ☞ iphone中App store上架优化建议

热门文章

  1. 使用Easyexcel动态生成excel
  2. 汇通汉诺塔益智 3.0
  3. 计算机毕业设计springboot化妆品商城网站
  4. 2022考研写作不过如此pdf电子版
  5. Visio2007退出崩溃
  6. 如何恢复vscode的默认配置_VS恢复默认设置的2种方法
  7. ESP32-S2 SGM58031-热敏电阻电压转温度
  8. win10 安装office 2016 plus 备忘
  9. stm32学习记录——继电器
  10. ReactiveX简介