详细图解高阶搜索树B树及C语言实现

本文详细介绍了B树的概念,包括其作为多路平衡树的特性,以及在磁盘I/O优化中的作用。文章通过2-3-4树为例,讲解了B树的查找、插入和删除操作,并提供了C语言的实现思路。强调了B树在处理大量数据时的效率优势,并指出B树的插入和删除操作遵循自平衡规则,确保树的高度可控。
摘要由CSDN通过智能技术生成

目录

一、什么是B-tree

二、查找

三、插入

四、删除


前言
对于大量的数据而言,链表的线性访问时间太慢,不宜使用。本章节将会介绍一种简单的数据结构:树(tree),其大部分操作的运行时间平均为O(logN)。在数据结构中树是非常有用的抽象概念,在本篇中我们将讨论一棵高阶搜索树——b树。前排提示:高阶搜索树的代码难度高出一个量级,建议先把逻辑理顺后再去看代码哦

一、什么是B-tree

虽然迄今为止我们所看到的查找树都是二叉树,但是还有一种常用的查找树不是二叉结构的,这种树叫做B树(B-tree)。

那么问题来了,既然已经有了诸如AVL树的查找树,为什么还要存在B树呢?因为平衡树的数据放在内存当中,但在实际的项目中,数据量特别的大,要放在磁盘中,而面临大量数据时二叉树的高度大意味着与磁盘交互次数多,而每一次磁盘的I/O都是机械运动,读取速度相对于内存来说是很慢的,为了降低树的高度、减少磁盘I/O,B树就此诞生。

B树具有下列特性:

  • 是一棵多路平衡树,一个节点有多个数据,通常要求空间大小和磁盘块的大小一致;
  • 在逻辑上,所有的叶子都在同一层且不带信息;
  • 对于节点规定了上界和下界,最常见的每个节点(根节点除外)包含【t-1,2t-1】个关键字(其中t是最小的度,t>=2),不是硬性规定,根据实际中的磁盘块来决定;
  • 如果一个节点有x个关键字,就有x+1个孩子;
  • 每一个节点的关键字按照升序排序,x和y之间孩子的范围就是(x, y)。

为了使大家在概念上更方便理解,正如先前在二叉排序树中做的那样,我们使实际的数据存储在叶子上,也可以存储在内部节点中。实际上B树有多种定义,这些定义在一些次要的细节上不同于我们定义的结构。如下所示就是一棵B树:

这里还要提一个概念,图中的B树是4阶B树的一个例子,它的更流行的称呼是2-3-4树,而3阶B树称作2-3树;我们将通过这棵2-3-4树来描述B树增删查改。

二、查找

B树的查找和二叉排序树的查找过程十分相似,唯一不同的是:二叉排序树的任意一个节点最多只能有两个孩子,而B树则最多可以有M+1个孩子(M为节点中关键字个数),但这并不影响我们用与二叉排序树相同的查找方法,只需要多一步在节点中的遍历(可以是顺序也可以是二分,方便就行),我在代码中对返回的结果做了封装,这样过程会更加规范,当然效果是一样的。

/*查找返回节点下标的函数 从1到keynum找key*/
int search(BTree p, KeyType key)
{
	int i = 1;
	while (i <= p->keynum && key > p->key[i])
	{
		i++;
	}
	return i;
}

/*返回封装的结果集的查找函数 不是真正进行查找步骤*/
void searchNode(BTree tree, KeyType key, Result& r)
{
	int i;
	int found = 0;//标记查找成功或者失败 成功是1 失败是0
	//先定义指针指向根节点
	BTree p = tree;
	BTree q = NULL;//有时候需要指向双亲节点的指针
	while (p != NULL)
	{
		i = search(p, key);
		if (i <= p->keynum && key == p->key[i])
		{
			//不允许重复所以找到就是查找失败 说明这个数据已经存进去了
			found = 1;
		}
		else
		{
			q = p;
			p = p->ptr[i - 1];//指针下移
		}
	}
	//如果查找到了
	if (found == 1)
	{
		r.pt = p;
		r.i = i;
		r.tag = found;
	}
	//没有查找到
	else
	{
		//没有找到就把父节点传回去 利于后续操作
		r.pt = q;
		r.i = i;
		r.tag = found;
	}
}

三、插入

B树的插入规则与二叉排序树的插入类似,即一个节点中的关键字总是有序的,并且总是要插入到实际的叶子节点;不同的是,为了满足B树的高度可控这一特性,需要时刻判断是否超过了规定的存储最大容量,如果达到容量上限就要进行分裂(split);

有如上一棵2-3树,当我们试图插入新关键字K时,发现K所属的节点已经满了,插入K将使得这个节点拥有四个关键字,这是不允许的,这时我们就要分裂,从中间对半分,得到两个新的节点;

但此时我们又发现,分裂出的新节点的父节点现在有了四个孩子,而它只有两个关键字,最多只能有三个孩子,这样又带来了一个新的问题,当然解决的办法也很简单,我们可以把中间值上移,是父节点的关键变成三个,这样父节点最大就能有四个孩子了。

分裂是维持B树性质的重要操作之一,一定要把逻辑理清楚!接下来我们对上文中的2-3-4树再过一遍完整的插入流程:

第一步:判断根节点是否初始化,如果没有,初始化root为根节点;

第二步:当我们插入节点i,与根节点p比较,小于就往左边插入;

第三步:发现p的左孩子节点已经满了,需要分裂;

第四步:继续比较,比L小,插L的左子树;

第五步:判断L的左子树是否满了,满了就分裂,没满就挨个比较找到正确的插入位置插入,结束。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白还在写代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值