B+树详解与实现

B+树详解与实现

  • 一、引言
  • 二、B+树的定义
  • 三、B+树的插入
  • 四、B+树的删除
  • 五、B+树的查找效率
  • 六、B+树与B树的区别和联系

一、引言

B+树是一种树数据结构,通常用于数据库和操作系统的文件系统中。它的特点是能够保持数据稳定有序,其插入与修改拥有较稳定的对数时间复杂度。B+树元素自底向上插入,这与二叉树恰好相反。B+树在节点访问时间远远超过节点内部访问的时候,比可作为替代的实现有着实在的优势。通过最大化在每个内部节点内的子节点的数目减少树的高度,平衡操作不经常发生,而且效率增加了。这种价值得以确立通常需要每个节点在次级存储中占据完整的磁盘块或近似的大小。B+树是B树的一种变形形式,B+树上的叶子结点存储关键字以及相应记录的地址,叶子结点以上各层作为索引使用。一棵m阶的B+树和m阶的B树的差异在于:

  1. 有n棵子树的节点中含有n个关键字(B树中是n-1个)。
  2. 所有的叶子节点中包含了全部关键字的信息以及指向这些关键字记录的指针,且叶子节点本身依关键字的大小从小到大顺序链接。
  3. 所有的非叶子节点可以看成是索引部分,节点中仅含有其子树(根节点)中的最大(或最小)关键字。

通常在B+树上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点。对B+树进行查找时,通常从根节点开始,当找到叶子节点后,再在其中进行查找,直到找到对应的数据或指针。与B-树相比,B+树在非叶子节点上是不存储数据的,只存储它的孩子节点的最大(或最小)关键字信息和指向其子节点的指针信息,这样使得B+树非叶子节点所能容纳的孩子节点信息更多,树的高度相对比B-树小,在查找时所需要的IO操作次数也比B-树小。

在这里插入图片描述

二、B+树的定义

一颗m阶的B+树和m阶的B树的差异在于:

  1. 有n棵子树的节点中含有n个关键字。(B树中是n-1个)
  2. 所有的叶子节点中包含了全部关键字的信息以及指向含这些关键字记录的指针,且叶子节点本身依关键字的大小从小到大顺序链接。
  3. 所有的非叶子节点可以看成是索引部分,节点中仅含有其子树(根节点)中的最大(或最小)关键字。

三、B+树的插入

B+树的插入首先在叶子节点中进行,若插入后叶子节点中的关键字个数大于m,则需要进行分裂操作。分裂需要两个步骤:

  1. 节点分裂:将原节点中的关键字和子节点平均分配到两个新的节点中,原节点中的第m/2个(下取整,下同)关键字上升到其父节点中(父节点中关键字的个数加1),若没有父节点(原节点为根节点),则创建一个新的根节点。
  2. 调整索引:当分裂操作使得节点中关键字的个数超过m-1个时,需要对父节点进行分裂操作(即使父节点的关键字个数没有超过m-1,但只要其有子节点的关键字个数超过m-1,也需要进行分裂,以保证B+树的性质)。这个分裂过程可能会递归向上进行,甚至可能导致根节点的分裂,从而增加树的高度。

以下是插入操作的伪代码实现:

function Insert(root, key) {leaf = FindLeaf(root, key)if (leaf.keyword_count < order - 1) {// 插入到叶子节点leaf.Insert(key)} else {// 分裂叶子节点new_leaf, median = leaf.Split()parent = leaf.parentif (parent == null) {// 创建新的根节点new_root = CreateNewRoot(leaf, new_leaf, median)root = new_root} else {parent.Insert(median)if (parent.keyword_count > order - 1) {Insert(root, median) // 递归插入}}}
}

四、B+树的删除

B+树的删除操作稍微复杂一些,需要考虑多种情况。如果被删除的关键字位于非叶子节点,则需要用其后继节点(或前驱节点)中的最小(或最大)关键字替换,并转化为在叶子节点中的删除。如果被删除的关键字位于叶子节点,且该叶子节点中的关键字个数大于等于ceil(m/2),则可以直接删除。否则,需要考虑合并叶子节点或者从相邻节点借调关键字。以下是删除操作的伪代码实现:

function Delete(root, key) {node = FindNode(root, key) // 找到包含key的节点if (node is not a leaf) {// 如果不是叶子节点,找到后继节点,并用后继节点中的最小关键字替换当前节点的key,然后删除后继节点中的该关键字successor = FindSuccessor(node) node.key = successor.min_key node = successor // 转化为在叶子节点中的删除}if (node.keyword_count > ceil(m/2)) {node.Delete(key) // 可以直接删除} else {if (node的相邻兄弟节点的关键字个数 > ceil(m/2)) {// 从相邻兄弟节点借调关键字BorrowFromSibling(node)} else {// 合并节点MergeWithSibling(node)}}
}

五、B+树的查找效率

在B+树上进行查找的效率与树的高度成正比,而树的高度又与树的阶数m和包含n个关键字的B+树的层数相关。因此,通过合理设置B+树的阶数m,可以使得查找效率达到最优。在实际情况中,通常根据磁盘块的大小和关键字的平均大小来确定B+树的阶数m。

六、B+树与B树的区别和联系

B+树与B树的主要区别在于非叶子节点是否存储关键字信息。在B树中,非叶子节点既存储关键字信息,又存储子节点的指针信息;而在B+树中,非叶子节点只存储子节点的最大(或最小)关键字信息和指针信息,所有的关键字信息和相应的数据都存储在叶子节点中。这使得B+树的非叶子节点可以存储更多的子节点信息,从而降低了树的高度,提高了查找效率。另外,B+树的叶子节点之间是通过指针链接在一起的,这样便于进行范围查找和顺序访问。

七、C语言实现B+树的基本操作示例(部分代码)

为了提供一个更完整的示例,下面是一个简化的B+树插入操作的C代码实现。请注意,这个实现是为了教学目的而简化的,并不适合用于生产环境。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>#define ORDER 4 // B+树的阶数
#define MAX_KEYS (ORDER - 1)
#define MIN_KEYS (ORDER / 2)typedef struct BPlusTreeNode {int keys[MAX_KEYS];struct BPlusTreeNode *children[ORDER];struct BPlusTreeNode *next; // 用于叶子节点的链表int keyCount;bool isLeaf;
} BPlusTreeNode;BPlusTreeNode* createNode(bool isLeaf) {BPlusTreeNode* newNode = (BPlusTreeNode*)malloc(sizeof(BPlusTreeNode));if (!newNode) {perror("Memory allocation failed");exit(EXIT_FAILURE);}newNode->keyCount = 0;newNode->isLeaf = isLeaf;newNode->next = NULL;for (int i = 0; i < ORDER; i++) {newNode->children[i] = NULL;}return newNode;
}void insertKey(BPlusTreeNode* node, int key, BPlusTreeNode* child) {int i;for (i = node->keyCount - 1; i >= 0 && key < node->keys[i]; i--) {node->keys[i + 1] = node->keys[i];if (!node->isLeaf) {node->children[i + 2] = node->children[i + 1];}}node->keys[i + 1] = key;if (!node->isLeaf) {node->children[i + 2] = child;}node->keyCount++;
}void splitNode(BPlusTreeNode* node, BPlusTreeNode** newNode, int* median) {*newNode = createNode(node->isLeaf);int midIndex = node->keyCount / 2;*median = node->keys[midIndex];(*newNode)->keyCount = node->keyCount - midIndex - 1;for (int i = 0; i < (*newNode)->keyCount; i++) {(*newNode)->keys[i] = node->keys[midIndex + 1 + i];if (node->isLeaf) {// If it's a leaf node, copy the next pointers as well(*newNode)->next = node->next;node->next = NULL;} else {(*newNode)->children[i] = node->children[midIndex + 1 + i];}}node->keyCount = midIndex + 1;
}// Recursive insert function
void insertRecursive(BPlusTreeNode* node, int key, BPlusTreeNode* leafNode) {if (node->isLeaf) {insertKey(node, key, leafNode);if (node->keyCount > MAX_KEYS) {BPlusTreeNode* newNode;int median;splitNode(node, &newNode, &median);if (node->next) {newNode->next = node->next;node->next->keys[0] = median; // Set the smallest key of the next nodenode->next = newNode;}}} else {int i;for (i = 0; i < node->keyCount; i++) {if (key < node->keys[i]) {break;}}if (i < node->keyCount) {insertRecursive(node->children[i + 1], key, leafNode);if (node->children[i + 1]->keyCount > MAX_KEYS) {int median;BPlusTreeNode* newNode;splitNode(node->children[i + 1], &newNode, &median);insertKey(node, median, newNode);}} else {insertRecursive(node->children[i], key, leafNode);if (node->children[i]->keyCount > MAX_KEYS) {int median;BPlusTreeNode* newNode;splitNode(node->children[i], &newNode, &median);insertKey(node, median, newNode);}}}
}void insert(BPlusTreeNode** root, int key) {BPlusTreeNode* leafNode = createNode(true); // Dummy leaf node for recursionif (*root == NULL) {*root = createNode(true);insertKey(*root, key, leafNode);} else {insertRecursive(*root, key, leafNode);}
}// Main function to test the insert operation
int main() {BPlusTreeNode* root = NULL;int keys[] = {10, 20, 5, 15, 30, 7, 17, 25, 40};int n = sizeof(keys) / sizeof(keys[0]);for (int i = 0; i < n; i++) {insert(&root, keys[i]);}// Simple traversal to print the keys in the B+ tree (only for leaves)BPlusTreeNode* curr = root;while (!curr->isLeaf) {curr = curr->children[0]; // Assuming the smallest key is in the leftmost leaf}printf("Leaf nodes contain: ");while (curr) {for (int i = 0; i < curr->keyCount; i++) {printf("%d ", curr->keys[i]);}printf("| ");curr = curr->next;}printf("\n");// Cleanup code would go here (recursively free all allocated nodes)return 0;
}

这个简化的示例展示了如何在B+树中插入关键字。请注意,这个实现没有处理删除操作,没有实现完整的查找功能,也没有进行错误检查或优化。此外,为了简化代码,我们假设所有叶子节点都在同一个层级上,且我们省略了叶子节点中指向记录的指针(在这个示例中不需要)。在实际应用中,B+树的实现会更加复杂,并需要考虑更多的边界情况和性能优化。

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

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

相关文章

ngrinder项目-本地调试遇到的坑

前提-maven mirrors配置 <mirrors><!--阿里公有仓库--><mirror><id>nexus-aliyun</id><mirrorOf>central</mirrorOf><name>Nexus aliyun</name><url>http://maven.aliyun.com/nexus/content/groups/public</ur…

借助Aspose.SVG图像控件,在线将 PNG 转换为 XML

Aspose.SVG for .NET 是用于SVG文件处理的灵活库&#xff0c;并且与其规范完全兼容。API可以轻松加载&#xff0c;保存和转换SVG文件&#xff0c;以及通过其文档对象模型&#xff08;DOM&#xff09;读取和遍历文件的元素。API独立于任何其他软件&#xff0c;使开发人员无需使用…

分布式与一致性协议之Raft算法(一)

Raft算法 概述 Raft算法属于Multi-Paxos算法&#xff0c;它在兰伯特Multi-Paxos思想的基础上做了一些简化和限制&#xff0c;比如日志必须是连续的&#xff0c;只支持领导者(Leader)、跟随者(Follwer)和候选人(Candidate)3种状态。在理解和算法实现上&#xff0c;Raft算法相对…

基于Springboot的大学生社团活动平台

基于SpringbootVue的大学生社团活动平台设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 首页 社团信息 社团活动 社团论坛 社团资讯 后台登录 后台首页 学生管理 社…

医疗大模型华佗GPT-2:医学问答超越GPT-4,通过2023年国家执业药师考试

前言 随着人工智能技术的快速发展&#xff0c;特别是在自然语言处理(NLP)领域&#xff0c;大型预训练模型如GPT系列已经显示出在多个领域的强大应用潜力。最近&#xff0c;华佗GPT-2医疗大模型的发布&#xff0c;不仅标志着人工智能在医学领域的一大进步&#xff0c;更是在202…

Mybatis逆向工程的2种方法,一键高效快速生成Pojo、Mapper、XML,摆脱大量重复开发

一、写在开头 最近一直在更新《Java成长计划》这个专栏&#xff0c;主要是Java全流程学习的一个记录&#xff0c;目前已经更新到Java并发多线程部分&#xff0c;后续会继续更新&#xff1b;而今天准备开设一个全新的专栏 《EfficientFarm》。 EfficientFarm&#xff1a;高效农…

eaapp账号注销怎么操作 简单几步完成ea账号注销

eaapp账号注销怎么操作 简单几步完成ea账号注销 有许多玩家在注册ea平台账户的时候&#xff0c;会出现账号输错&#xff0c;地区选择错误等问题&#xff0c;导致自己没有注册成功心仪的账号&#xff0c;想要将账号注销却又不知道该如何操作&#xff0c;今天小编就为大家带来详…

mac idea 下载spring 源码遇到的问题

一、Kotlin: warnings found and -Werror specified 这个问题网上看了很多文章多说是缺少cglib、objenesis包。然后执行了 实际还是没有什么用 解决&#xff1a; 最后自己看了一下前面一个警告。说的就是版本太低。所以我觉得是这个前置问题导致的 然后搜索了改这个Kotlin版本…

springboot项目组合定时器schedule注解实现定时任务

springboot项目组合定时器schedule注解实现定时任务&#xff01; 创建好springboot项目后&#xff0c;需要在启动类上增加注解开启定时器任务 下图所示&#xff1a; 增加这个注解&#xff0c;启动项目&#xff0c; package com.example.scheduledemo.util;import org.springf…

C++-5

完成特殊成员函数 #include <iostream>using namespace std;class Person {string name;int *age; public://构造&#xff0c;析构&#xff0c;拷贝构造&#xff0c;拷贝赋值Person():age(new int ){}Person(int *age,string name):name(name),age(new int (*(age))){}~…

unity制作app(2)--主界面

1.先跳转过来&#xff0c;做一个空壳&#xff01;新增场景main为4号场景&#xff01; 2.登录成功跳转到四号场景&#xff01; 2.在main场景中新建canvas&#xff0c;不同的状态计划用不同的panel来设计&#xff01; 增加canvas和底图image 3.突然输不出来中文了&#xff0c;浪…

【C语言加油站】字符函数与字符串函数

字符函数与字符串函数 导言一、字符分类函数1.1 字符分类函数的用法 二、字符转换函数2.1 字符转换函数的用法 三、字符串函数3.1 成员3.2 strlen函数3.2.1 size_t类型3.2.2 strlen的易错点3.2.2 strlen的使用3.2.3 strlen与sizeof 3.3 strcpy函数和strncpy函数3.3.1 strcpy和s…

unity 专项一 localPosition与anchoredPosition(3D)的区别

一 、RectTransform 概念 1、RectTransform继承自Transform&#xff0c;用于描述矩形的坐标(Position)&#xff0c;尺寸(Size)&#xff0c;锚点(anchor)和中心点(pivot)等信息&#xff0c;每个2D布局下的元素都会自动生成该组件。 2、当我们在处理UI组件时&#xff0c;往往容易…

VTK 的可视化方法:Glyph

VTK 的可视化方法&#xff1a;Glyph VTK 的可视化方法&#xff1a;Glyph标量、向量、张量将多边形数据的采集点法向量标记成锥形符号参考 VTK 的可视化方法&#xff1a;Glyph 模型的法向量数据是向量数据&#xff0c;因此法向量不能像前面讲到的通过颜色映射来显示。但是可以通…

【计算机网络】网络层总结

目录 知识梗概 IP地址 子网划分 IP包头格式 路由 网络层协议 ARP病毒/ARP欺骗 知识梗概 IP地址 IP相关介绍&#xff1a;机器之间需要交流&#xff0c;必须要一个地址才能找到对应的主机&#xff0c;IP地址是主机的一种表示&#xff0c;保证主机之间的正常通信&#xff…

LabVIEW高效目标跟踪系统

LabVIEW高效目标跟踪系统 随着机器视觉技术的飞速发展&#xff0c;设计和实现高效的目标跟踪系统成为了众多领域关注的焦点。基于LabVIEW平台&#xff0c;结合NI Vision机器视觉库&#xff0c;开发了一种既高效又灵活的目标跟踪系统。通过面向对象编程方法和队列消息处理器程序…

File contains parsing errors: file:///etc/yum.repos.d/nginx.repo报错解决,文件配置出现问题

执行yum指令出现以下错误&#xff1a; 解决方案&#xff1a;yum的配置文件出现问题&#xff0c; 先删除yum.repos.d目录下所有文件 rm -f /etc/yum.repos.d/* 然后重新下载阿里的资源 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.…

无人机+大载重+长航时:油电混动多旋翼无人机技术详解

多旋翼无人机是一种具有三个及以上旋翼轴的特殊的无人驾驶旋翼飞行器。具有稳定性强、操控简单、勤务性高、价格便宜等优势&#xff0c;因此在市场上的应用非常广泛。此外&#xff0c;利用地面供电的绳系多旋翼通过电缆向多旋翼持续传输电能&#xff0c;可以大大提高多旋翼的空…

【Java EE】MyBatis使用注解操作数据库

文章目录 &#x1f340;参数传递&#x1f334;增(Insert)&#x1f338;返回主键 &#x1f343;删(Delete)&#x1f333;改(Update)&#x1f332;查(Select)&#x1f338;起别名&#x1f338;结果映射&#x1f338;开启驼峰命名(推荐) ⭕总结 &#x1f340;参数传递 需求: 查找…

邊緣智能2024—AI開發者峰會(5月9日)數碼港即將啟幕

隨著 AI &#xff08;人工智能&#xff09;技術的飛速發展&#xff0c;我們正迎來邊緣計算智能化與分布式AI深度融合的新時代&#xff0c;共同演繹分布式智能創新應用的壯麗篇章。"邊緣智能2024 - AI開發者峰會"將聚焦於這一前沿領域&#xff0c;探討如何通過邊緣計算…