B树(数据结构篇)

news2025/7/5 15:32:29

数据结构之B树

B-树(B-tree)

概念

  • B-树是一个非二叉树多路平衡查找树(数据有序),是一颗所有数据都存储在树叶节点上的树,不一定存储具体的数据,也可以是指向包含数据的记录的指针或地址

  • 对于**阶为M(子节点数量在2和M之间)**的B-树具有一下结构特性:

    1. 树的根节点或者叶子节点,或者子节点数的范围为[2,M]
    2. B树每个结点关键字数量为[ceil(2/M)-1,M-1]
    3. 除根外,所有非树叶节点的子节点数在[ceil(2/M),M]之间,ceil为向上取整函数
    4. 所有的树叶都在相同的深度上
  • 非叶子节点也有存储关键词的地方,这个地方是来表示范围的(如果要查找的数据小于该关键字就找关键字左边的子节点数据,大于就右边的子节点数据),如果叶子节点数据按照升序排列,则非叶子节点的关键词有m-1个(m为该非叶子节点的子节点个数),如果按照降序排列,则非叶子节点的关键字有m个,例如图4-58为升序排列的B树

    image

插入操作

  • 插入在叶子节点进行

  • 向B树中插入数据,根据非叶子节点的关键字找到正确的存储数据的叶子节点

  • 如果节点内的数据小于M-1(M为阶数),就根据排列规则插入;如果节点能够存储的数据已经满了,就进行分裂节点(叶子节点)

    分裂节点操作步骤:

    1. 先看看该叶子节点的关键字数量是否小于M-1(M为阶数)
    2. 按顺序插入进去,节点的关键字数量如果大于M-1,就将该叶子节点分裂成两个叶子节点,两个叶子节点的数据各占原叶子节点的一半中间的关键字(数据)作为根节点的关键字剩下分成两部分的节点作为其(中间关键字形成的根节点)左右节点。当根节点大于M-1的时候,就分裂根节点!
    3. 如果小于则根据插入的关键字大小按顺序插入。

删除操作

  1. 通过递归找到指定删除的节点

  2. 删除的关键字在非叶子节点上,就将其左边的指向叶子节点中的最大值跟要删除的关键字互换,就递归进去删除最大值。

  3. 删除的关键字在叶子节点上

    1. 叶子节点的关键字个数大于ceil(M/2-1),直接删除
    2. 叶子节点的关键字个数等于ceil(M/2-1),向父节点借关键字
  4. 递归返回上一层后检查该节点的关键字数,如果小于ceil(M/2-1),就向父节点借关键字,合并该关键字的左右节点,然后不断返回上一层,不断地检查,向父节点借关键字,合并子节点直到返回到根节点。

代码:

#include <iostream>
#include <vector>
using namespace std;

struct btree{
    int level;  //树的阶数
    vector<int>keys;  //关键字数组
    vector<btree*>child; //子节点数组
    int keynum;   //节点的关键字数目
    btree* parent;    //子节点的父节点
};

//创造节点
btree* createNode(int level){
    auto p=new btree;
    p->level=level;
    p->keynum=0;
    p->parent= nullptr;
    for(int i=0;i<=p->level;i++){
        p->child.push_back(nullptr);
        p->keys.push_back(0);
    }
    return p;
}

//查找指定值,返回一个包含节点指针,和指定值的位置的对组
pair<btree*,int> find(btree* root,int key){
    int i;
    for(i=root->keynum;i>0;i--){
        if(key<root->keys[i]){
            continue;
        }else if(key>root->keys[i]){
            break;
        }else{
            return make_pair(root,i);
        }
    }
    pair<btree*,int>p=find(root->child[i],key);
    return p;
}

//分裂节点
void splitNode(btree* &root,btree* p,int index){
    if(p!=nullptr){
        if(p->keynum==p->level){
            //分裂节点
            int mid=p->keynum/2+p->keynum%2;
            btree* newnode= createNode(p->level);
            newnode->parent=p->parent;
            for(int j=root->keynum;j>index-1;j--){
                root->keys[j+1]=root->keys[j];
            }
            root->keys[index]=p->keys[mid];
            root->keynum++;
            for(int j=mid+1;j<=p->keynum;j++){
                newnode->keys[j-mid]=p->keys[j];
                newnode->keynum++;
            }
            p->keynum=p->keynum-newnode->keynum-1;
            int k;
            for(k=root->level-1;k>index-1;k--){
                root->child[k+1]=root->child[k];
            }
            k++;
            root->child[k]=newnode;
        }
    }
    if(root->keynum==root->level) {
        btree *newchild = createNode(root->level);
        int mid = root->keynum / 2 + root->keynum % 2;
        for (int i = mid + 1; i <= root->level; i++) {
            newchild->keys[i - mid] = root->keys[i];
            newchild->keynum++;
        }
        for (int j = newchild->keynum; j <= newchild->level; j++) {
            if(root->child[j])
                root->child[j]->parent=newchild;
            newchild->child[j - newchild->keynum] = root->child[j];
            root->child[j] = nullptr;
        }
        if (root->parent == nullptr) {
            btree *newnode = createNode(root->level);
            newnode->keys[1] = root->keys[mid];
            newnode->keynum++;
            root->keynum = root->level - newchild->keynum - 1;
            newnode->child[0] = root;
            newnode->child[1] = newchild;
            root->parent = newnode;
            newchild->parent = newnode;
            root = newnode;
        } else {
            newchild->parent = root->parent;
            root->keynum = root->level - newchild->keynum - 1;
            int a = root->parent->keynum;
            while (a > 0 && root->keys[mid] < root->parent->keys[a]) {
                root->parent->keys[a + 1] = root->parent->keys[a];
                root->parent->child[a + 1] = root->parent->child[a];
                a--;
            }
            a++;
            root->parent->keys[a] = root->keys[mid];
            root->parent->keynum++;
            root->parent->child[a] = newchild;
        }
    }
}

//插入节点
btree* insert(btree* &root,int key){
    if(0==root->keynum){
        root->keys[1]=key;
        root->keynum++;
        return root;
    }
    int index=root->keynum;
    while (index>0&&key<root->keys[index]){
        root->keys[index+1]=root->keys[index];
        index--;
    }
    index++;
    if(root->child[0]!=nullptr){
        btree* p;
        if(index==root->keynum){
            p=root->child[index+1];
        }else{
            p=root->child[index-1];
        }
        if(root->child[0]->child[0]!=nullptr){
            p= insert(p,key);
        }else if(root->child[0]->child[0]==nullptr){
            int i=p->keynum;
            while (i>0&&key<p->keys[i]){
                p->keys[i+1]=p->keys[i];
                i--;
            }
            i++;
            p->keys[i]=key;
            p->keynum++;
        }
        splitNode(root,p,index);
    }else{
        root->keys[index]=key;
        root->keynum++;
        splitNode(root, nullptr,-1);
    }
    return root;
}

//查找最大值
int findmax(btree* root){
    if(nullptr==root){
        return 0;
    }else if(root->child[0]!= nullptr){
        return findmax(root->child[root->keynum]);
    }
    return root->keys[root->keynum];
}

//合并节点
void merge(btree* &root,int key,int min,int n){
    int n1 = root->child[n-1]->keynum;
    int n2 = root->child[n]->keynum;
    if (n1 > min) {
        for (int j = n2; j > 0; j--) {
            root->child[n]->keys[j + 1] = root->child[n]->keys[j];
            root->child[n]->child[j + 1] = root->child[n]->child[j];
        }
        root->child[n]->child[1] = root->child[n]->child[0];
        root->child[n]->keys[1] = root->keys[n];
        root->keys[n]=root->child[n-1]->keys[n1];
        root->child[n]->child[0] = root->child[n-1]->child[n1];
        root->child[n-1]->child[n1] = nullptr;
        root->child[n-1]->child[0]->parent = root->child[n-1];
        root->child[n-1]->keynum--;
        root->child[n-1]->keys[n1] = NULL;
        root->child[n]->keynum++;
    } else if (n2 > min) {
        root->child[n-1]->keys[n1+1]=root->keys[n];
        root->keys[n]=root->child[n]->keys[1];
        root->child[n-1]->child[n1 + 1] = root->child[n]->child[0];
        root->child[n-1]->child[n1 + 1]->parent = root->child[n-1];
        root->child[n-1]->keynum++;
        for (int j = 1; j < n2; j++) {
            root->child[n]->keys[j] = root->child[n]->keys[j + 1];
            root->child[n]->child[j - 1] = root->child[n]->child[j];
        }
        root->child[n]->child[n2-1]=root->child[n]->child[n2];
        root->child[n]->keys[n2] = NULL;
        root->child[n]->child[n2] = nullptr;
        root->child[n]->keynum--;
    } else {
        root->child[n-1]->keys[n1+1]=root->keys[n];
        root->child[n-1]->keynum++;
        int n3 = n2 + n1+1;
        for (int j = n1 + 2; j <= n3; j++) {
            root->child[n-1]->keys[j] = root->child[n]->keys[j - n1-1];
            root->child[n-1]->child[j-1]=root->child[n]->child[j];
            root->child[n-1]->keynum++;
        }
        root->child[n]=nullptr;
        int index = root->keynum;
        while (index > n && key < root->keys[index]) {
            root->keys[index-1]=root->keys[index];
            root->child[index-1]=root->child[index];
            index--;
        }
        root->child[root->keynum]= nullptr;
        root->keynum--;
        if(root->parent== nullptr&&root->keynum==0){
            root->child[0]->parent= nullptr;
            root=root->child[0];
        }
    }
}

//删除节点
void del(btree* &root,int key){
    if(nullptr==root){
        return;
    }else{
        int i;
        for(i=root->keynum;i>0;i--){
            if(key<root->keys[i]){
                continue;
            }else if(key>root->keys[i]){
                del(root->child[i],key);
            }else{
                break;
            }
        }
        int min=(root->level/2+root->level%2)-1;
        if(0==i){
            if(root->child[i]->keynum>=min&&root->child[i+1]->keynum>=min){
                del(root->child[i],key);
            }
            i++;
        }
        if(root->child[0]!= nullptr){
            if(root->keynum>=min){
                if(root->keys[i]==key){
                    int temp= findmax(root->child[i-1]);
                    root->keys[i]=temp;
                    del(root->child[i-1],temp);
                    merge(root,key,min,i);
                }else if(key<root->keys[i]){
                    if(root->child[i-1]->keynum<min){
                        merge(root,key,min,i);
                    }
                }else{
                    if(root->child[i]->keynum<min){
                        merge(root,key,min,i);
                    }
                }
            }else{
                merge(root,key,min,i);
            }
        }else{
            int j;
            for(j=1;j<root->keynum;j++){
                if(root->keys[j]==key){
                    break;
                }
            }
            for(int d=j;d<root->keynum;d++){
                root->keys[d]=root->keys[d+1];
            }
            root->keys[root->keynum]=NULL;
            if(root->keynum>min){
                root->keynum--;
            }else{
                root->keynum--;
                int index=root->parent->keynum;
                for(int k=root->keynum;k>0;k--){
                    root->keys[k+1]=root->keys[k];
                }
                while (index>0&&key<=root->parent->keys[index]){
                    index--;
                }
                if(0==index){
                    root->keys[root->keynum+1]=root->parent->keys[1];
                }else{
                    root->keys[root->keynum+1]=root->parent->keys[index];
                }
            }
        }
    }
}

//中序遍历
void inorderprint(btree* root){
    if(nullptr!=root){
        int i;
        for(i=0;i<root->keynum;i++){
            if(root->child[i]!= nullptr){
                inorderprint(root->child[i]);
            }
            cout<<root->keys[i+1]<<" ";
        }
        if(root->child[i]!= nullptr)
            inorderprint(root->child[i]);
    }
}

尾言

完整版笔记也就是数据结构与算法专栏完整版可到我的博客进行查看,或者在github库中自取(包含源代码)

  • 博客1: codebooks.xyz
  • 博客2:moonfordream.github.io
  • github项目地址:Data-Structure-and-Algorithms

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式数据库_2.嵌入式数据库的一般架构

嵌入式数据库的架构与应用对象紧密相关&#xff0c;其架构是以内存、文件和网络等三种方式为主。 1.基于内存的数据库系统 基于内存的数据库系统中比较典型的产品是每个McObject公司的eXtremeDB嵌入式数据库&#xff0c;2013年3月推出5.0版&#xff0c;它采用内存数据结构&…

Ansys Mechanical|学习方法

Ansys Mechanical是Ansys的旗舰产品之一&#xff0c;涉及的学科体系全面丰富&#xff0c;包括的力学分支主要有理论力学&#xff0c;振动理论&#xff0c;连续介质力学&#xff0c;固态力学&#xff0c;物理力学&#xff0c;爆炸力学及应用力学等。 在自媒体及数字经济飞速发展…

Samtec制造理念系列一 | 差异变量的概念

【摘要/前言】 制造高端电子产品是非常复杂精密的过程。制作用于演示或原型的一次性样品可能具有挑战性&#xff0c;但真正的挑战在于如何以盈利的方式持续生产。 这就是Samtec风险投资研发工程总监Aaron Tucker在一次关于生产高密度微小型连接器的挑战的演讲中所强调的观点。…

使用QMainWindow、QMenuBar,QToolBar文本编辑器界面布局设置

使用QMainWindow、QMenuBar&#xff0c;QToolBar设计一个文本编辑器的界面 菜单 菜单输入处输入 文件$F ,呈现文件(F),快捷键AltF ,打开文件菜单 添加工具栏 在窗体空白处&#xff0c;右键添加工具栏 Action工具 在Designer界面下方 批量定义action 拖入到menu和 toolBar中 Too…

Docker 拉取镜像失败处理 配置使用代理拉取

解决方案 1、在 /etc/systemd/system/docker.service.d/http-proxy.conf 配置文件中添加代理信息 2、重启docker服务 具体操作如下&#xff1a; 创建 dockerd 相关的 systemd 目录&#xff0c;这个目录下的配置将覆盖 dockerd 的默认配置 代码语言&#xff1a;javascript 复…

阿里云API文档有哪些实用功能?如何使用?

阿里云API安全性如何保障&#xff1f;阿里云API怎么实现自动化&#xff1f; 阿里云作为全球领先的云计算服务提供商&#xff0c;提供了广泛的API接口&#xff0c;以满足各类用户的需求。阿里云API文档不仅详尽&#xff0c;而且易于使用&#xff0c;AokSend将详细介绍阿里云API…

事务的实现机制

一、基础概念 事务&#xff08;Transaction&#xff09;是访问和更新数据库的程序执行单元&#xff1b;事务中可能包含一个或多个sql语句&#xff0c;这些语句要么都执行&#xff0c;要么都不执行。作为一个关系型数据库&#xff0c;MySQL支持事务&#xff0c; 事务&#xff…

关于IntelliJ IDEA 2024.1版本更新的问题

希望文章能给到你启发和灵感&#xff5e; 感谢支持和关注&#xff5e; 阅读指南 序幕一、基础环境说明1.1 硬件环境1.2 软件环境 二、起因三、解决四、总结 序幕 近期&#xff0c;IntelliJ IDEA 推出了全新2024版本&#xff0c;相信很多编程的爱好者或者刚接触编程的小伙伴都会…

【Linux】关于在华为云中开放了端口后仍然无法访问的问题

已在安全组中添加规则: 通过指令: netstat -nltp | head -2 && netstat -nltp | grep 8080 运行结果: 可以看到服务器确实处于监听状态了. 通过指令 telnet 公网ip port 也提示: "正在连接xxx.xx.xx.xxx...无法打开到主机的连接。 在端口 8080: 连接失败"…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 最长的指定瑕疵度的元音子串(100分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

【windows|007】DHCP服务详解

&#x1f341;博主简介&#xff1a; &#x1f3c5;云计算领域优质创作者 &#x1f3c5;2022年CSDN新星计划python赛道第一名 &#x1f3c5;2022年CSDN原力计划优质作者 ​ &#x1f3c5;阿里云ACE认证高级工程师 ​ &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社…

IDEA配置maven,热部署,生命周期和插件,maven工程的聚合和继承

1.maven配置 先来说maven配置 首先到官网下载 https://mvnrepository.com/search?qmaven 解压就可以 然后解压完会生成一个apache-maven-3.3.9的文件 我们配置一下阿里云的镜像仓库 D:\maven\apache-maven-3.3.9\conf 我的是这个&#xff0c;你们的和我不一样&#xff0c…

Talk|香港科技大学冯宸:高效自主的大尺度场景空中覆盖与重建

本期为TechBeat人工智能社区第601期线上Talk。 北京时间6月19日(周三)20:00&#xff0c;香港科技大学冯宸博士生—冯宸的Talk已经准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “高效自主的大尺度场景空中覆盖与重建”&#xff0c;他围绕团队在利用无人机…

【C++题解】1324 - 扩建鱼塘问题

问题&#xff1a;1324 - 扩建鱼塘问题 类型&#xff1a;分支问题 题目描述&#xff1a; 有一个尺寸为 mn 的矩形鱼塘&#xff0c;请问如果要把该鱼塘扩建为正方形&#xff0c;那么它的面积至少增加了多少平方米&#xff1f; 输入&#xff1a; 两个整数 m 和 n 。 输出&…

AI 已经在污染互联网了。。赛博喂屎成为现实

大家好&#xff0c;我是程序员鱼皮。这两年 AI 发展势头迅猛&#xff0c;更好的性能、更低的成本、更优的效果&#xff0c;让 AI 这一曾经高高在上的技术也走入大众的视野&#xff0c;能够被我们大多数普通人轻松使用&#xff0c;无需理解复杂的技术和原理。 其中&#xff0c;…

大语言模型的微调方法_大语言模型六种微调方法

01 引言 自2018年BERT发布以来&#xff0c;“预训练微调”成为语言模型的通用范式。以ChatGPT为代表的大语言模型针对不同任务构造Prompt来训练&#xff0c;本质上仍然是预训练与微调的使用范式。千亿规模的参数微调需要大量算力&#xff0c;即使提供了预训练的基座模型&…

RPM命令和YUM命令

目录 一、RPM软件包 1.1、RPM概述 1.2、查询已安装的rpm软件信息 1.3、查询未安装的 RPM 软件包文件中信息 1.4、安装、升级、卸载 RPM 软件包 二、YUM常规命令 三、手动配置Apache&#xff08;http&#xff09;服务 3.1、前提条件 3.2、开始配置 3.3、开启验证服务 …

YOLOv10改进 | 注意力篇 | YOLOv10引入iRMB

1. iRMB介绍 1.1 摘要:本文重点关注开发现代、高效、轻量级的模型来进行密集预测,同时权衡参数、FLOP 和性能。 反向残差块(IRB)作为轻量级 CNN 的基础设施,但基于注意力的研究尚未认识到对应的部分。 这项工作从统一的角度重新思考高效IRB和Transformer有效组件的轻量级…

更换域名流程记录

华为云的服务器&#xff0c;阿里云购买的域名。 1.购买域名 2.在域名服务商绑定服务器ip&#xff08;以阿里云为例&#xff09; 控制台->域名控制台->域名列表->点击域名->域名解析->添加记录 记录类型填A , 主机记录“”或“www”&#xff0c;记录值填服务器i…

自养号测评助亚马逊、速卖通店铺快速提升商品流量与转化率?

在全球化的浪潮下&#xff0c;跨境电商行业如日中天&#xff0c;速卖通作为行业的领军者&#xff0c;汇聚了世界各地的消费者与商家。要想在速卖通平台上成功打造爆款产品&#xff0c;我们需从多个维度出发&#xff0c;精心策划与实施。 一、产品选择的艺术 在速卖通上&#…