B树(数据结构篇)

数据结构之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.mzph.cn/pingmian/30697.shtml

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

相关文章

Properties与xml知识点总结

文章目录 一、Properties1.1 构造方法1.2 从Properties文件中获取1.3 向Properties文件中存储 二、xml2.1 XML2.2 特点2.3 规则2.3 抬头声明2.4 特殊字符2.5 CDATA区段2.4 作用和应用场景 三、区别 一、Properties 定义&#xff1a;properties是一个双列集合集合&#xff0c;拥…

android 在线程中更新界面

在Android中&#xff0c;你不能直接从子线程中更新UI&#xff0c;因为这会导致应用崩溃。你需要使用Handler或runOnUiThread()来更新UI。 使用Handler 以下是如何使用Handler在子线程中更新UI的示例&#xff1a; 1. 创建Handler实例&#xff1a; import android.os.Bundle;…

产品经理方法论

1、用户体验 5 要素 1&#xff0c;表现层是你拿到一个产品以后&#xff0c;视觉表现&#xff0c;配色&#xff0c;布局&#xff0c;排版等等 2&#xff0c;框架层&#xff0c;是交互层面的东西&#xff0c;比如&#xff0c;操作情况&#xff0c;刷新&#xff0c;页面跳转&…

ChatmoneyAI如狂风般席卷广告创意舞台,轻松闯荡财富之海!

本文由 ChatMoney团队出品 引言 在广告创意行业&#xff0c;创新和高效是赢得市场的关键。而我今天要分享的就是如何利用ChatmoneyAI这款强大的人工智能工具&#xff0c;打破创新难题&#xff0c;赚取丰厚收益。 让我告诉你一个小秘密&#xff0c;有客户曾在一个月内&#xf…

git merge(3个模式) 与 git rebase 图文详解区别

目录 1 git merge1.1 模式一&#xff1a;fast-forward(–ff)1.2 模式二&#xff1a;non-Fast-forward(–no-ff)1.3 模式三&#xff1a;fast-forward only(–ff-only) 2 git rebase3 区别 1 git merge git merge有好几种不同的模式 默认情况下你直接使用 git merge 命令&#x…

从boost库到时间戳

一、以问题引入 授权证书一般有到期时间的说法&#xff0c;公司测试同事在测试更新后的证书时&#xff0c;将系统时间调到了2050年&#xff0c;重启服务后发现各个进程的cpu占用率特别高&#xff1b;结合日志分析&#xff0c;发现这些进程 都在不停的刷heartbeat()的日志&#…

C++17并行算法与HIPSTDPAR

C17 parallel algorithms and HIPSTDPAR — ROCm Blogs (amd.com) C17标准在原有的C标准库中引入了并行算法的概念。像std::transform这样的并行版本算法保持了与常规串行版本相同的签名&#xff0c;只是增加了一个额外的参数来指定使用的执行策略。这种灵活性使得已经使用C标准…

AI 音乐大模型:创新的曙光还是创意产业的阴影?

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

[面试题]Kafka

[面试题]Java【基础】[面试题]Java【虚拟机】[面试题]Java【并发】[面试题]Java【集合】[面试题]MySQL[面试题]Maven[面试题]Spring Boot[面试题]Spring Cloud[面试题]Spring MVC[面试题]Spring[面试题]MyBatis[面试题]Nginx[面试题]缓存[面试题]Redis[面试题]消息队列[面试题]…

RAG(检索增强生成)的演变:初级 RAG、高级 RAG 和模块化 RAG 架构

大型语言模型&#xff08;LLMs&#xff09;通过在自然语言任务及其它领域的成功应用&#xff0c;如 ChatGPT、Bard、Claude 等所示&#xff0c;已经彻底改变了 AI 领域。这些 LLMs 能够生成从创意写作到复杂代码的文本。然而&#xff0c;LLMs 面临着幻觉、过时知识和不透明、无…

基于ChatGPT的大型语言模型试用心得

近年来&#xff0c;ChatGPT这样的大型语言模型&#xff0c;它如同一颗冉冉升起的新星&#xff0c;迅速在商业、教育、娱乐等多个领域照亮了创新的天空&#xff0c;极大地革新了我们的工作与日常生活。 最近我发现一些国内用户也能自由访问的中文ChatGPT APP。这个平台不仅提供…

10招教你玩转Python循环优化

更多Python学习内容&#xff1a;ipengtao.com 在Python编程中&#xff0c;循环是最常见的控制结构之一。尽管Python的循环语法简单明了&#xff0c;但在处理大量数据或进行复杂计算时&#xff0c;循环可能会成为性能瓶颈。本文将介绍10种加速Python循环的方法&#xff0c;帮助在…

[Linux] 系统的基本架构特点

Linux系统的基本结构 Linux is also a subversion of UNIX,it follows the basic structure of UNIX 内核(kernel)&#xff1a; 操作系统的基本部分 管理与硬件相关的功能&#xff0c;分模块进行 常驻模块&#xff1a;进程控制IO操作文件\磁盘访问 用户不能直接访问内核 外壳(s…

数据资产:打破数据孤岛,实现数据互联互通,构建企业智能化转型的重要桥梁。通过高效整合与利用数据资源,推动企业决策的科学化、精准化,助力企业迈向智能化新时代

目录 一、引言 二、数据孤岛现象及其影响 三、打破数据孤岛&#xff0c;实现数据互联互通 1、制定统一的数据标准和管理规范 2、建设统一的数据平台 3、推广数据共享和开放文化 四、数据资产在智能化转型中的重要作用 1、推动企业决策的科学化、精准化 2、优化企业运营…

盘点下常见 HDFS JournalNode 异常的问题原因和修复方法

盘点下常见 HDFS JournalNode 异常的问题原因和修复方法 最近在多个客户现场以及公司内部环境&#xff0c;都遇到了因为 JournalNode 异常导致 HDFS 服务不可用的问题&#xff0c;在此总结下相关知识。 1 HDFS HA 高可用和 JournalNode 概述 HDFS namenode 有 SPOF 单点故障…

【尚庭公寓SpringBoot + Vue 项目实战】移动端项目初始化(十九)

【尚庭公寓SpringBoot Vue 项目实战】移动端项目初始化&#xff08;十九&#xff09; 文章目录 【尚庭公寓SpringBoot Vue 项目实战】移动端项目初始化&#xff08;十九&#xff09;1、 SpringBoot配置2、Mybatis-Plus配置3、Knife4j配置4、导入基础代码5、导入接口定义代码6…

上海中腾食品科学餐饮管理铸就企业食堂新模式

在当今企业运营中&#xff0c;食堂不仅是员工用餐的场所&#xff0c;更是企业文化和管理水平的体现。随着餐饮行业的不断发展&#xff0c;科学合理的餐饮管理模式成为了企业食堂成功的关键。上海中腾食品科技有限公司以其独特的餐饮管理模式&#xff0c;成功打造了企业食堂的新…

CSS3中鲜为人知但非常强大的 Clip-Path 属性

CSS3中鲜为人知但非常强大的 Clip-Path 属性 在CSS3中,clip-path属性可以让我们快速创建各种各样的不规则图形,而无需使用图片或者复杂的绘图工具。它可以帮助我们实现一些非常出色的视觉效果,但遗憾的是它并不是很常见。 clip-path属性可以接受多种不同的值,比如polygon()、…

静态网页发送基本请求

目录 一、 发送 GET 请求 1&#xff0e;不携带 url 参数的 GET 请求 2&#xff0e;携带 url 参数的 GET 请求 二、发送 POST 请求 三、处理响应 1&#xff0e;获取网页源代码 2&#xff0e;获取图片 一、 发送 GET 请求 当用户在浏览器的地址栏中直接输入某个 URL 地址…

海量数据处理利器 Roaring BitMap 原理介绍

作者&#xff1a;来自 vivo 互联网服务器团队- Zheng Rui 本文结合个人理解梳理了BitMap及Roaring BitMap的原理及使用&#xff0c;分别主要介绍了Roaring BitMap的存储方式及三种container类型及Java中Roaring BitMap相关API使用。 一、引言 在进行大数据开发时&#xff0c;…