首页 > 编程语言 >高阶数据结构--B树&&B+树实现原理&&B树模拟实现--Java

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

时间:2024-12-08 14:27:58浏览次数:7  
标签:Java parent -- keys 索引 && BTreeNode root 节点

目录

一、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域。 聚集索引这种实现方式使得按主键的搜索十分高效 ,但是 辅助索引搜索需要检索两遍索引:首先检索辅助索引获得 主键,然后用主键到主索引中检索获得记录。

标签:Java,parent,--,keys,索引,&&,BTreeNode,root,节点
From: https://blog.csdn.net/2301_79995226/article/details/144324721

相关文章

  • 《Python 图神经网络编程全指南》
    《Python图神经网络编程全指南》一、引言Python中的图神经网络编程正逐渐成为数据科学和机器学习领域的热门话题。随着数据的日益复杂和多样化,传统的数据分析方法往往难以有效地处理具有复杂关系结构的数据。而图神经网络作为一种新兴的技术,能够很好地捕捉图结构数据中......
  • AT_arc188_a [ARC188A] ABC Symmetry 题解
    容易发现一个串是好的的充要条件是:A,B,C出现次数的奇偶性都相同。因此我们也可以将所有的串分为四类:好的,只有A和其他两个的奇偶性不同,只有B和其他两个的奇偶性不同,只有C和其他两个的奇偶性不同。大于\(k\)的不好统计,可以直接用总数减去小于\(k\)的总和。设$f_{i,x,......
  • 【牛客训练记录】浙江机电职业技术大学第九届程序设计竞赛
    训练情况赛后反思还得是ZJ爷上强度,全场只会两题,还是太菜了A题每个人可以发起两次拼团,每个人只能接受一次别人的拼团,因为每个人都可以发起拼团,并且有两次机会,所以我们可以不用在意发起拼团的限制,自己机会用完了可以让别人发起,所以我们只需要计算剩余的\(2\)个人有多少对,每......
  • 实验5
    实验任务1.1:源代码:1#include<stdio.h>2#defineN534voidinput(intx[],intn);5voidoutput(intx[],intn);6voidfind_min_max(intx[],intn,int*pmin,int*pmax);78intmain(){9inta[N];10intmin,max;1112pri......
  • 三着急教你爆改烂大街项目
    简历上如何写项目最近收到最多的提问就是,简历上应该写什么项目,应该准备什么项目,项目经历怎么写到简历上.如果你简历上没有实习经历简历就写一个业务项目一个轮子项目.如果你有一段实习经历那就先写你实习的项目,再写一个业务项目和一个轮子项目.如果你有两段以上的......
  • 第77篇 SQL Server数据库如何优化
    前言在SQLServer中,当数据量增大时,数据库的性能可能会受到影响,导致查询速度变慢、响应时间变长等问题。为了应对大量数据,以下是一些常用的优化策略和案例详解1.索引优化创建索引:索引可以显著提高查询速度,特别是在使用WHERE、JOIN和ORDERBY子句时。为常用的查询字段(尤其......
  • 蓝桥杯 | 报数游戏 - 第十五届蓝桥杯大赛软件赛省赛Java 大学 B 组真题
    问题描述小蓝和朋友们在玩一个报数游戏。由于今年是2024年,他们决定要从小到大轮流报出是20或24倍数的正整数。前10个被报出的数是:20,24,40,48,60,72,80,96,100,120。请问第202420242024个被报出的数是多少?解题思路方案一importjava.util.Scanner;//1:......
  • 分库分表—4.数据迁移系统文档d3
    大纲1.数据库设计2.枚举类3.接⼝设计4.定时任务设计(1)定时核对校验数据的定时任务(2)数据量统计定时任务(3)增量数据落地定时任务(4)失败重试定时任务5.技术亮点(1)滚动拉取方案(2)巧妙的统计滚动进度方案(3)防止增量同步数据丢失和高效写入方案(4)数据扩容方案6.......
  • 【牛客训练记录】牛客小白月赛106
    训练情况赛后反思由于是周五晚上,C题猜了一个假结论,做到后面摆了A题这题只关心答案的奇偶性,我们注意到偶数的非零次方是偶数,奇数的非零次方是奇数,所以我们就不需要求幂,但是注意一下偶数的\(0\)次方会改变奇偶性。#include<bits/stdc++.h>#defineintlonglong#definee......
  • 基于人工智能的摔倒识别摄像头
    摔倒识别摄像头技术的核心在于利用深度学习算法对摄像头捕获的视频进行实时分析。通过训练模型,系统能够学习人体摔倒的特征,包括身体姿势、动作轨迹等。一旦检测到可能的摔倒行为,系统会触发警报,通知相关人员或安防系统。摄像头通过高清晰度视频采集场景信息,将图像传输到后端处理系......