B树的实现

今天我们就来实现以下B树,B树有什么特点那?我们来列举一下

  • 每个非叶子节点中存放若干关键字数据,并且有若干指向儿子节点的指针。指针数目=关键字数目+1
  • 根节点有最少1个,最多m-1个关键字,最少2个,最多m个子节点。
  • 非根节点最少有m/2,最多m-1个关键字
  • 每个节点中的关键字从左到右以非降序排列
  • 每个关键字均不小于其左子节点的关键字,不大于其右子节点的所有关键字
  • 每个叶子节点都具有相同的深度
B树的节点的增加

我们还是通过1-25个数的增加,来探索一下,B树的增加节点有什么规律,并写出代码。

首先我们定义出来我们B树的结构,如下:

#define DEGREE		3
typedef int KEY_VALUE; typedef struct _BTREE_NODE
{
KEY_VALUE* keys;
struct _BTREE_NODE** Childrens;
int num;
int leaf;
}BTREE_NODE,*PBTREE_NODE; typedef struct _BTREE
{
BTREE_NODE* root;
int t;
};

我们来看一下B树1-20个数字的增加的图片

1-5



6-10



11-15



16-20

我们首先需要创建一个根节点:

BTREE_NODE* btree_create_node(int t, int leaf) {

    BTREE_NODE* node = (BTREE_NODE*)calloc(1, sizeof(BTREE_NODE));
if (node == NULL) assert(0); node->leaf = leaf;
node->keys = (KEY_VALUE*)calloc(1, (2 * t - 1) * sizeof(KEY_VALUE));
node->Childrens = (BTREE_NODE**)calloc(1, (2 * t) * sizeof(BTREE_NODE*));
node->num = 0; return node;
}
//创建根节点
void btree_create(BTREE*T,int t)
{
T->t = t;
PBTREE_NODE x = btree_create_node(t, 1);
T->root = x;
}

现在就写一下我们插入的代码

BTREE_NODE* btree_create_node(int t, int leaf) {

    BTREE_NODE* node = (BTREE_NODE*)calloc(1, sizeof(BTREE_NODE));
if (node == NULL) assert(0); node->leaf = leaf;
node->keys = (KEY_VALUE*)calloc(1, (2 * t - 1) * sizeof(KEY_VALUE));
node->Childrens = (BTREE_NODE**)calloc(1, (2 * t) * sizeof(BTREE_NODE*));
node->num = 0; return node;
}
//节点分裂
void btree_split_child(BTREE* T,BTREE_NODE* x,int i)
{
int t = T->t; BTREE_NODE* y = x->Childrens[i];
BTREE_NODE* z = btree_create_node(t, y->leaf);
z->num = t - 1; int j = 0;
for (j=0;j<t-1;j++)
{
z->keys[j] = y->keys[j + t];
}
if (y->leaf==0)
{
for (j=0;j<t;j++)
{
z->Childrens[j] = y->Childrens[j + t];
}
}
y->num = t - 1;
for (j=x->num;j>=i+1;j--)
{
x->Childrens[j + 1] = x->Childrens[j];
}
x->Childrens[i + 1] = z;
for(j=x->num-1;j>=i;j--)
{
x->keys[j + 1] = x->keys[j];
}
x->keys[i] = y->keys[t - 1];
x->num += 1;
}
//创建节点
void btree_create(BTREE*T,int t)
{
T->t = t;
PBTREE_NODE x = btree_create_node(t, 1);
T->root = x;
}
void btree_insert_notfull(BTREE*T,BTREE_NODE *x,KEY_VALUE k)
{
//获取节点数量,从0开始减1
int i = x->num-1;
//只有1个叶子节点
if (x->leaf==1)
{
while (i>=0&&x->keys[i]>k)
{
x->keys[i + 1] = x->keys[i];
i--;
}
//赋值
x->keys[i + 1] = k;
x->num += 1;
}else
{
//找到应该插入的叶子节点
while (i >= 0 && x->keys[i] > k) i--;
//是否已经满了5个节点
if (x->Childrens[i+1]->num==((2*T->t))-1)
{
btree_split_child(T, x, i + 1);
if (k>x->keys[i+1])
{
i++;
}
} btree_insert_notfull(T, x->Childrens[i + 1], k); }
}
void btree_insert(BTREE *T ,KEY_VALUE key)
{
//获取头节点
BTREE_NODE* r = T->root;
//如果满节点就要进行这里的操作 if (r->num==2*T->t-1)
{
BTREE_NODE* node = btree_create_node(T->t, 0);
T->root = node; node->Childrens[0] = r; btree_split_child(T, node, 0); int i = 0;
if (node->keys[0] < key) i++;
btree_insert_notfull(T, node->Childrens[i], key);
}
else
{
//如果没有满就要进行这里的操作
btree_insert_notfull(T, r, key);
} }
B树节点的删除

我们还是看一下是如何删除的示意图,然后再写代码。

这里主要讨论一下删除的几种情况,









B树删除的代码

//释放节点
void btree_destory_node(BTREE_NODE* node)
{
if (node == nullptr)
{
return;
}
free(node->Childrens);
free(node->keys);
free(node);
}
void btree_merge(BTREE* T, BTREE_NODE* node, int idx)
{
BTREE_NODE* left = node->Childrens[idx];
BTREE_NODE* right = node->Childrens[idx + 1]; int i = 0;
left->keys[T->t - 1] = node->keys[idx];
//开始数据的合并
for (i = 0; i < T->t - 1; i++)
{
left->keys[T->t + 1] = right->keys[i];
}
if (!left->leaf)
{
for (i = 0; i < T->t; i++)
{
left->Childrens[T->t + 1] = right->Childrens[i];
}
}
left->num += T->t;
//合并完成摧毁节点
btree_destory_node(right); //node
for (i = idx + 1; i < node->num; i++)
{
node->keys[i - 1] = node->keys[i];
node->Childrens[i] = node->Childrens[i + 1];
}
node->Childrens[i + 1] = NULL;
node->num -= 1;
if (node->num == 0)
{
T->root = left;
btree_destory_node(node);
} }
void btree_delete_key(BTREE* T, BTREE_NODE* node, KEY_VALUE key)
{
//如果是空节点,直接返回
if (node == nullptr)
{
return;
}
int idx = 0, i;
//获取key所在的位置
while (idx<node->num && key>node->keys[idx])
{
idx++;
}
if (idx < node->num && key == node->keys[idx])
{
if (node->leaf)
{
//如果是叶子节点,直接删除
for (i = idx; i < node->num - 1; i++)
{
node->keys[i] = node->keys[i + 1];
}
node->keys[node->num - 1] = 0;
node->num--;
//如果是根节点的情况
if (node->num == 0)
{
free(node);
T->root = nullptr;
}
return;
}//直接删除
else if (node->Childrens[idx]->num >= T->t)
{
BTREE_NODE* left = node->Childrens[idx];
node->keys[idx] = left->keys[left->num - 1];
btree_delete_key(T, left, left->keys[left->num - 1]);
}//直接删除
else if (node->Childrens[idx + 1]->num >= T->t)
{
BTREE_NODE* right = node->Childrens[idx + 1];
node->keys[idx] = right->keys[0];
btree_delete_key(T, right, right->keys[0]); }
else {
//如果都不是,说明是左右孩子节点都是T-1个关键字
btree_merge(T, node, idx);
btree_delete_key(T, node->Childrens[idx], key);
}
}
else
{
BTREE_NODE* child = node->Childrens[idx];
if (child == NULL)
{
printf("Can\'t del key=%d\n", key);
return;
}//子节点的数目刚好等于2
if (child->num == T->t - 1)
{
BTREE_NODE* left = nullptr;
BTREE_NODE* right = nullptr;
if (idx - 1 >= 0)
{
left = node->Childrens[idx - 1];
}
if (idx + 1 <= node->num)
{
right = node->Childrens[idx + 1];
}
//如果左右节点任何一个都可以借用节点
if ((left && left->num >= T->t) || (right && right->num >= T->t))
{
int richR = 0;
if (right)
{
richR = 1;
}
if (left && right)
{
richR = (right->num > left->num) ? 1 : 0;
}
//从右借用节点
if (right && right->num >= T->t && richR)
{
child->keys[child->num] = node->keys[idx];
child->Childrens[child->num + 1] = right->Childrens[0];
child->num++;
node->keys[idx] = right->keys[0];
//调整右边的节点
for (i = 0; i < right->num - 1; i++)
{
right->keys[i] = right->keys[i + 1];
right->Childrens[i] = right->Childrens[i + 1];
} right->keys[right->num - 1] = 0;
right->Childrens[right->num - 1] = right->Childrens[right->num];
right->Childrens[right->num] = NULL;
right->num--;
}
else
{
//从左借节点
for (i = child->num; i > 0; i--)
{
child->keys[i] = child->keys[i - 1];
child->Childrens[i + 1] = child->Childrens[i];
}
child->Childrens[1] = child->Childrens[0];
child->Childrens[0] = left->Childrens[left->num];
child->keys[0] = node->keys[idx - 1];
child->num++; node->keys[idx - 1] = left->keys[left->num - 1];
left->keys[left->num - 1] = 0;
left->Childrens[left->num] = NULL;
left->num--;
} }
else if ((!left) || (left->num == T->t - 1) && (!right) || (right->num == T->t - 1))
{
if (left&&left->num==T->t-1)
{
btree_merge(T, node, idx - 1);
child = left;
}else if(right&&right->num==T->t-1)
{
btree_merge(T, node, idx);
}
}
btree_delete_key(T, child, key);
}
} }
int btree_delete(BTREE* T, KEY_VALUE key)
{
if (!T->root)
{
return -1;
}
btree_delete_key(T, T->root, key);
return 0;
}

推荐一个零声学院免费教程,个人觉得老师讲得不错,

分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,

fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,

TCP/IP,协程,DPDK等技术内容,点击立即学习:

服务器

音视频

dpdk

Linux内核

C++B树的实现的更多相关文章

  1. B树——算法导论(25)

    B树 1. 简介 在之前我们学习了红黑树,今天再学习一种树--B树.它与红黑树有许多类似的地方,比如都是平衡搜索树,但它们在功能和结构上却有较大的差别. 从功能上看,B树是为磁盘或其他存储设备设计的, ...

  2. ASP.NET Aries 入门开发教程8:树型列表及自定义右键菜单

    前言: 前面几篇重点都在讲普通列表的相关操作. 本篇主要讲树型列表的操作. 框架在设计时,已经把树型列表和普通列表全面统一了操作,用法几乎是一致的. 下面介绍一些差距化的内容: 1:树型列表绑定: v ...

  3. 再讲IQueryable<T>,揭开表达式树的神秘面纱

    接上篇<先说IEnumerable,我们每天用的foreach你真的懂它吗?> 最近园子里定制自己的orm那是一个风生水起,感觉不整个自己的orm都不好意思继续混博客园了(开个玩笑).那么 ...

  4. HDU1671——前缀树的一点感触

    题目http://acm.hdu.edu.cn/showproblem.php?pid=1671 题目本身不难,一棵前缀树OK,但是前两次提交都没有成功. 第一次Memory Limit Exceed ...

  5. 算法与数据结构(十一) 平衡二叉树(AVL树)

    今天的博客是在上一篇博客的基础上进行的延伸.上一篇博客我们主要聊了二叉排序树,详情请戳<二叉排序树的查找.插入与删除>.本篇博客我们就在二叉排序树的基础上来聊聊平衡二叉树,也叫AVL树,A ...

  6. [C#] C# 知识回顾 - 表达式树 Expression Trees

    C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达 ...

  7. bzoj3207--Hash+主席树

    题目大意: 给定一个n个数的序列和m个询问(n,m<=100000)和k,每个询问包含k+2个数字:l,r,b[1],b[2]...b[k],要求输出b[1]~b[k]在[l,r]中是否出现. ...

  8. bzoj1901--树状数组套主席树

    树状数组套主席树模板题... 题目大意: 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]--a[ ...

  9. bzoj3932--可持久化线段树

    题目大意: 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第 ...

  10. jquery-treegrid树状表格的使用(.Net平台)

    上一篇介绍了DataTable,这一篇在DT的基础之上再使用jquery的一款插件:treegrid,官网地址:http://maxazan.github.io/jquery-treegrid/ 一. ...

随机推荐

  1. centos7.9离线安装MongoDB4.4.17

    前言 MongoDB 5.0开始要求CPU支持avx指令集,参考https://mp.weixin.qq.com/s/6FFXih1DEZYDFOk1hCu69w 环境 CentOS 7.9.2009 ...

  2. 顶配涨至近2万 该买还是买!iPhone15正面曝光 与历代苹果手机对比边框爆窄

    从曝光的iPhone 15正面渲染图来看,其颜值确实要比上代又提高不少. 外媒放出了一组iPhone 15 Pro的正面渲染图照,从图片看边框非常的窄,与历代iPhone 边框对比,这个特点更是被放大 ...

  3. Kafka-分布式安装

    一.kafka是什么? Kafka是一个快速.可扩展的.高吞吐.可容错的分布式发布订阅消息系统.Kafka具有高吞吐量.内置分区.支持数据副本和容错的特性,它可以处理消费者规模的网站中的所有动作流数据 ...

  4. HBase-表的压缩

    一.如何选择压缩算法以及Data_Block_Encoding?(1)如果Key很长,或者有很多Column,那么推荐使用FAST_DIFF.(2)如果数据是冷数据,不经常被访问,那么使用GZIP压缩 ...

  5. Linux 将命令的输出保存到文件

    当你在 Linux 终端中运行命令或脚本时,它会在终端中打印输出方便你立即查看.方法 1:使用重定向将命令输出保存到文件中你可以在 Linux 中使用重定向来达成目的.使用重定向操作符,它会将输出保存 ...

  6. JOISC 2022 记录

    Day1 T1 Jail 操作很类似华容道.由于这题是可以树,同时每一个人走的都是最短路,这也就意味着不会出现通过好多个人一起的挪动来匀出空间. 所以如果合法,必然存在一种方案是每一次直接将一个人挪到 ...

  7. 一个让java程序员有杀人的冲动的Xerces冲突问题

    History Xerces是Java生态圈使用最广泛的XML解析器,基本上所有的类库和框架都会在一定程度上使用它(即使没有直接引用,也有可能间接引用) Xerces在官网中发布的包是没有标注版本的, ...

  8. Idea 本人开发常用几款插件

    先说 idea装插件 首先,进入插件安装界面: 标注 1:显示 IntelliJ IDEA 的插件分类, All plugins:显示 IntelliJ IDEA 支持的所有插件: Enabled:显 ...

  9. 解决idea中使用git fetch报远程仓库Authentication failed

    这个问题是项目组同事在从git服务器clone代码后做fetch操作老是报错: Authentication failed, 弹出框提示:invalid user or password 其实问题出i ...

  10. acm数学总结

    1.给定两个质数,m, n, 大于n * m - n - m的数都可以被整数个n和m唯一组成. 相关习题:[Coins] (https://ac.nowcoder.com/acm/contest/34 ...