B+树的概念和操作笔记(含完整代码实现)

注:B+树叶子节点的概念又恢复为了最后一个节点是叶子节点,其他都是非叶子节点

B树:节点中存放关键字和数据指针(外存地址)

为了使得树变得更矮,

就要想办法在一个节点中存放更多的数据---非叶子节点中只存放关键字,不再跟着数据指针

叶子节点中关键字和数据指针一起放,并且上层节点中的数据 只是下层节点数据的索引,数据地址都在叶子节点中找

m阶B+树的性质:

1.非叶子节点中只存放关键字,不再跟着数据指针

叶子节点中关键字和数据指针一起放,并且上层节点中的数据 只是下层节点数据的索引,数据地址都在叶子节点中找

2.B+树中 关键字的个数和孩子节点的个数相同(代码里面下标可以一一对应)

因为父亲中的每一个关键字都会作为孩子的索引--->作为孩子节点的最大值

3.B+树有两个阶 分别控制非叶子节点和叶子节点。 但是考研统一 m阶B+树

毕竟一个指针存放节点地址,一个存放外存地址,还需要一个标志来区分它俩

4.查找操作一定要查找叶子节点上(复杂度恒为logn )

5.叶子节点之间通过next指针相连,类似链表---->方便区间查找

比如查找15,80是否存在:对15进行一次单点查找,然后就从叶子节点15开始继续往后遍历

6.每个非叶子节点最多有m棵子树,最多m个关键字

7.非叶 根节点最少有两颗子树, 其他内部节点至少有ceil(m/2)棵子树

总结优势:

1.同样大小的节点,B+树存放的关键字更多,阶更大,相同的数据量时,建立的B+树要比B树矮得多,查找更快

2.B+树的叶子节点之间通过指针相连,支持区间查找和顺序查找

B+树的操作:

0.建树: 节点结构体,B+树结构体,结果结构体

可参考

typedef int KEY; //关键字类型 typedef int VALUE;//真实数据类型

1.单点查找:和B树的查找一样,只不过到叶子节点才可以停

2.区间查找: 先对左端点进行单点查找,然后顺着叶子节点进行顺序查找即可

3.插入数据k: 和B树的插入的步骤基本一样:先查找到位置<节点位置,下标位置>,然后插入,向上调整

  • 分裂是分两半(B树分三部分) 并把每部分的最大值复制到父节点
  • 向上调整的过程中根节点超出上限了:除了分裂,新添加一个根节点
  • 叶子节点的分裂需要维护next指针
  • 如果插入的是最大值(比叶子节点最右边的数据还大),要向上修改祖先们的节点中的关键字

(为了避免插入最大值时向上修改关键字,在具体实现B+树时,可以不存最大孩子的索引)

理论上孩子数=关键字数,但是实践过程中可以变化

4.删除操作:和B树一样。兄弟够借就借,不够借就合并。

直到不低于下限或者到根节点,孩子合并之后根节点只有一个关键字,删掉根节点。 结束

代码实现:

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define item_cmp(a,b) ((a) - (b))

/********************B+树的代码实现****************************/
typedef int KEY;
typedef int VALUE;
typedef struct  bplus_node_s* bplus_node_pt;
//B+树节点的定义
typedef struct bplus_node_s {
	KEY* keys;//主键 最大max个 最小min个 
	VALUE* data;//真实数据  最大max个 最小min个 对于内部结点 data是NULL
	bplus_node_pt* child;//子节点 最大max+1个 最小min+1个
	bplus_node_pt next;//指向兄弟结点 仅仅是叶子结点的时候才有值 
	int keynum;//当前元素个数
	bplus_node_pt parent;
}bplus_node_t;

typedef struct bplus_tree_s* bplus_tree_pt;

//B+树的定义
typedef struct bplus_tree_s {
	bplus_node_pt root;
	int max;
	int min;
}bplus_tree_t;
/******************************函数声明**************************************/
int bplus_tree_create(bplus_tree_pt* _tree, int m);//B+树的创建
int bplus_tree_insert(bplus_tree_pt tree, KEY key, VALUE value);
int binary_search(KEY* keys, KEY key, int left, int right, int* index);
bplus_node_pt bplus_node_new_leaf(int m);
int _bplus_tree_insert(bplus_tree_pt tree, bplus_node_pt node, KEY key, VALUE value);
bplus_node_pt bplus_node_new_internal(int m);
void bplus_tree_printf(bplus_tree_pt tree);
void bplus_node_show(bplus_node_pt node, int h);
void bplus_node_printfnode(KEY* key, int h);

void bplus_tree_delete(bplus_tree_pt tree, KEY key);


/******************************函数实现**************************************/

int bplus_tree_create(bplus_tree_pt* _tree, int m)
{
	bplus_tree_pt tree = (bplus_tree_pt)malloc(sizeof(bplus_tree_t));
	if (tree == NULL)
	{
		return 0;
	}
	tree->root = NULL;
	tree->max = m;
	tree->min = m / 2;
	*_tree = tree;
	return 1;
}

/*
二分查找 找到数组中指定的key的位置 如果存在返回1 否则返回0
如果找到索引为该值的位置 否则返回右邻的值的位置(child的位置)
*/
int binary_search(KEY* keys, KEY key, int left, int right, int* index)
{
	int mid;
	while (left <= right)
	{
		mid = (left + right) / 2;
		if (item_cmp(keys[mid],key) > 0)
		{
			right = mid - 1;
		}
		else if (item_cmp(keys[mid], key) < 0)
		{
			left = mid + 1;
		}
		else
		{
			*index = mid;
			return 1;
		}
	}
	*index = left;
	return 0;
}

/*
创建新的叶子结点
*/
bplus_node_pt bplus_node_new_leaf(int m)
{
	bplus_node_pt node = (bplus_node_pt)malloc(sizeof(bplus_node_t));
	if (node == NULL)
	{
		return NULL;
	}
	node->parent = NULL;
	node->next = NULL;
	node->keynum = 0;
	node->keys = (KEY*)malloc(sizeof(KEY) * (m + 1));
	node->data = (VALUE*)malloc(sizeof(VALUE) * (m + 1));
	if (node->keys == NULL || node->data == NULL)
	{
		free(node->keys);
		free(node->data);
		free(node);
		return NULL;
	}
	node->child = NULL;
	return node;


}

/*
创建新的内部结点
*/
bplus_node_pt bplus_node_new_internal(int m)
{
	bplus_node_pt node = (bplus_node_pt)malloc(sizeof(bplus_node_t));
	if (node == NULL)
	{
		return NULL;
	}
	node->parent = NULL;
	node->next = NULL;
	node->keynum = 0;
	node->keys = (KEY*)malloc(sizeof(KEY) * (m + 1));
	node->data = NULL;
	node->child = (bplus_node_pt*)malloc(sizeof(bplus_node_pt)*(m + 2));;
	if (node->keys == NULL || node->child == NULL)
	{
		free(node->keys);
		free(node->child);
		free(node);
		return NULL;
	}
	
	return node;

}

/*
元素的插入以及分裂操作
*/
int _bplus_tree_insert(bplus_tree_pt tree, bplus_node_pt node, KEY key, VALUE value)
{
	bplus_node_pt parent ,node2;
	int mid;
	KEY temp;
	int i;
	/*先插入叶子结点*/
	for (i = node->keynum; i > 0 && item_cmp(node->keys[i - 1],key) > 0; i--)
	{
		node->keys[i] = node->keys[i - 1];
		node->data[i] = node->data[i - 1];
	}
	node->keys[i] = key;
	node->data[i] = value;
	node->keynum++;
	while (node->keynum > tree->max)
	{
		/*分裂结点*/
		if (node->child == NULL)
		{
			//叶子结点分裂
			node2 = bplus_node_new_leaf(tree->max);
		}
		else {
			//内部结点分裂
			node2 = bplus_node_new_internal (tree->max);
		}
		if (node2 == NULL)
		{
			return 0;
		}
		//拷贝数据
		mid = node->keynum / 2;
		temp = node->keys[mid];
		if (node->child == NULL)
		{
			node2->keynum = node->keynum - mid;
			memcpy(node2->keys, node->keys + mid, sizeof(KEY) * (node2->keynum));
			memcpy(node2->data, node->data + mid, sizeof(KEY) * (node2->keynum));
			node2->next = node->next;
			node->next = node2;

		}
		else
		{

			node2->keynum = node->keynum - mid - 1;

			memcpy(node2->keys, node->keys + mid + 1, sizeof(KEY) * (node2->keynum));
			memcpy(node2->child, node->child + mid + 1, sizeof(bplus_node_pt) * (node->keynum - mid));

			//重新设置父指针
			for (int i = 0; i <= node2->keynum; i++)
			{
				node2->child[i]->parent = node2;
			}


		}

		node->keynum = mid;

		/*插入父节点*/
		parent = node->parent;
		if (parent == NULL)
		{
			//生成一个新的根节点
			parent = bplus_node_new_internal(tree->max);
			if (parent == NULL)
			{
				return 0;
			}
			parent->child[0] = node;
			node->parent = parent;
			tree->root = parent;
		}
		
		/*增加数据 右子树*/
		for (i = parent->keynum; i > 0 && item_cmp(parent->keys[i - 1], temp) > 0; i--)
		{
			parent->keys[i] = parent->keys[i - 1];
			parent->child[i + 1] = parent->child[i];
		}

		parent->keys[i] = temp;
		parent->child[i + 1] = node2;
		parent->keynum++;

		node2->parent = parent;

		//回溯 向上继续判断
		node = parent;


	}
	return 1;
}


int bplus_tree_insert(bplus_tree_pt tree, KEY key, VALUE value)
{
	bplus_node_pt node;
	int ret,index;
	if (tree->root == NULL)
	{
		node = bplus_node_new_leaf(tree->max);
		if (node == NULL)
		{
			return 0;
		}
		tree->root = node;
	}

	node = tree->root;
	//查找叶子结点
	while (node->child != NULL)
	{
		ret = binary_search(node->keys, key, 0, node->keynum - 1, &index);
		if (ret == 1)
		{
			index++;
		}
		node = node->child[index];
	}

	ret = binary_search(node->keys, key, 0, node->keynum - 1, &index);
	if (ret == 1)
	{
		//这个结点已经存在了
		return 0;
	}
	_bplus_tree_insert(tree, node, key, value);
	return 1;
}



void bplus_node_printfnode(KEY* key, int h)
{
	for (int  i = 0; i < h; i++)
	{
		printf("   ");
	}
	if (key == NULL)
	{
		printf("\n");
	}
	else {
		printf("%d\n", *key);
	}
}

void bplus_node_show(bplus_node_pt node, int h)
{
	if (node == NULL)
	{
		return;
	}
	if (node->child != NULL)
	{
		bplus_node_show(node->child[0], h + 1);
	}
	for (int i = 0; i < node->keynum; i++)
	{
		bplus_node_printfnode(&node->keys[i], h);
		if (node->child != NULL)
		{
			bplus_node_show(node->child[i + 1], h + 1);
		}
	}
}

void bplus_tree_printf(bplus_tree_pt tree)
{
	bplus_node_show(tree->root, 0);
}

/*

叶子结点的左旋转 把右节点的第一个值移到左节点
*/
void _bplus_leaf_left_rotate(bplus_node_pt node,int index)
{
	bplus_node_pt left, right;
	left = node->child[index];
	right = node->child[index + 1];
	left->keys[left->keynum] = right->keys[0];
	left->data[left->keynum] = right->data[0];
	left->keynum++;

	//对右节点进行移位
	for (int  i = 0; i < right->keynum - 1; i++)
	{
		right->keys[i] = right->keys[i + 1];
		right->data[i] = right->data[i + 1];
	}

	right->keynum--;

	node->keys[index] = right->keys[0];
}

/*

内部结点的左旋转 把右节点的第一个值移到父节点 父节点对应的值移到左节点
*/
void _bplus_internal_left_rotate(bplus_node_pt node, int index)
{
	bplus_node_pt left, right;
	left = node->child[index];
	right = node->child[index + 1];

	left->keys[left->keynum] = node->keys[index];
	left->child[left->keynum + 1] = right->child[0];
	left->keynum++;

	node->keys[index] = right->keys[0];


	//对右节点进行移位
	for (int i = 0; i < right->keynum - 1; i++)
	{
		right->keys[i] = right->keys[i + 1];
	}

	for (int i = 0; i < right->keynum; i++)
	{
		right->child[i] = right->child[i + 1];
	}

	right->keynum--;

}

/*
合并结点元素的左右两个子节点
*/
void _bplus_node_merge(bplus_node_pt node, int index)
{
	bplus_node_pt left, right;
	left = node->child[index];
	right = node->child[index + 1];
	if (left->child != NULL)
	{
		/*修改左子节点*/
		left->keys[left->keynum] = node->keys[index];
		memcpy(left->keys + left->keynum + 1, right->keys, sizeof(KEY) * (right->keynum));
		
		for (int i = 0; i <= right->keynum; i++)
		{
			right->child[i]->parent = left;
			left->child[left->keynum + i + 1] = right->child[i];
		}

		left->keynum = left->keynum + right->keynum + 1;
	}
	else
	{
		memcpy(left->keys + left->keynum, right->keys, sizeof(KEY) * (right->keynum));
		memcpy(left->data + left->keynum, right->data, sizeof(VALUE) * (right->keynum));

		left->keynum = left->keynum + right->keynum;;
	}

	/*修改父节点*/
	for (int i = index; i < node->keynum - 1; i++)
	{
		node->keys[i] = node->keys[i + 1];
	}

	for (int i = index + 1; i < node->keynum; i++)
	{
		node->child[i] = node->child[i + 1];
	}
	node->keynum--;

	/*释放右节点*/
	free(right->keys);
	free(right->data);
	free(right->child);
	free(right);

}

/*
叶子结点的右旋转
*/
void _bplus_leaf_right_rotate(bplus_node_pt node, int index)
{
	bplus_node_pt left, right;
	left = node->child[index];
	right = node->child[index + 1];

	for (int i = right->keynum; i > 0; i--)
	{
		right->keys[i] = right->keys[i - 1];
		right->data[i] = right->data[i - 1];
	}
	right->keynum++;
	right->keys[0] = left->keys[left->keynum - 1];
	right->data[0] = left->data[left->keynum - 1];

	left->keynum--;
	node->keys[index] = right->keys[0];

}

/*
内部结点的右旋转 左节点的最后一个值到父节点 父节点对应的值到右节点
*/
void _bplus_internal_right_rotate(bplus_node_pt node, int index)
{
	bplus_node_pt left, right;
	left = node->child[index];
	right = node->child[index + 1];
	for (int  i = right->keynum; i > 0; i--)
	{
		right->keys[i] = right->keys[i - 1];
	}
	right->keys[0] = node->keys[index];
	for (int i = right->keynum + 1; i > 0; i--)
	{
		right->child[i] = right->child[i - 1];
	}
	right->child[0] = left->child[left->keynum];
	right->keynum++;

	node->keys[index] = left->keys[left->keynum - 1];
	left->keynum--;


}


void _bplus_tree_delete(bplus_tree_pt tree, bplus_node_pt node, int index)
{
	bplus_node_pt parent, sibling;
	/*先删除叶子结点的指定的值*/
	for (int i = index; i < node->keynum - 1; i++)
	{
		node->keys[i] = node->keys[i + 1];
		node->data[i] = node->data[i + 1];
	}
	node->keynum--;
	parent = node->parent;
	while (node->keynum < tree->min  &&  parent!= NULL)
	{
		/*寻找到当前结点在父节点当中的位置*/
		for ( index = 0; index <= parent->keynum && parent->child[index] != node; index++);
		if (index > parent->keynum)
		{
			//在他的父节点中没找到这个孩子
			return;
		}
		if (index == 0)
		{
			//如果是第一个结点  没有左兄弟 只有右兄弟
			sibling = parent->child[1];
			if (sibling->keynum > tree->min)
			{
				//迁移数据
				if (node->child == NULL)
				{
					//叶子结点的迁移
					_bplus_leaf_left_rotate(parent, 0);
				}
				else {
					_bplus_internal_left_rotate(parent, 0);

				}

			}
			else
			{
				/*合并兄弟*/
				_bplus_node_merge(parent, 0);
			}
		}
		else
		{
			/*
			如果结点是父节中的第index个子节点,兄弟是第index - 1
			中间的分割元素是父节点中的第index - 1个元素
			*/
			sibling = parent->child[index - 1];
			if (sibling->keynum > tree->min)
			{
				//从左兄弟迁移数据
				if (node->child == NULL)
				{
					//叶子结点的右旋转
					_bplus_leaf_right_rotate(parent, index - 1);
				}
				else {
					//内部结点的右旋转
					_bplus_internal_right_rotate(parent, index - 1);
				}

			}
			else
			{
				/*合并兄弟*/
				_bplus_node_merge(parent, index - 1);
			}


		}

		node = parent;
		parent = node->parent;
		
	}

	if (tree->root->keynum == 0 && tree->root->child != NULL)
	{
		node = tree->root;
		tree->root = node->child[0];
		free(node->keys);
		free(node->data);
		free(node->child);
		free(node);
	}



}


void bplus_tree_delete(bplus_tree_pt tree, KEY key)
{
	bplus_node_pt node = tree->root;
	int ret = 0, index;
	/*先查找到叶子*/
	while (node->child != NULL)
	{
		ret = binary_search(node->keys, key, 0, node->keynum - 1, &index);
		if (ret == 1)
		{
			index++;
		}
		node = node->child[index];
	}
	ret = binary_search(node->keys, key, 0, node->keynum - 1, &index);
	if (ret == 0)
	{
		return;
	}

	_bplus_tree_delete(tree,node,index);

	return;

}



int main()
{
	bplus_tree_pt tree;
	int ret;
	ret = bplus_tree_create(&tree, 5);
	for (int i = 0; i < 30; i++)
	{
		bplus_tree_insert(tree, i, 1);
	}
	bplus_tree_printf(tree);

	printf("\n删除元素\n");

	bplus_tree_delete(tree, 1);
	bplus_tree_delete(tree, 2);
	bplus_tree_delete(tree, 3);
	bplus_tree_delete(tree, 4);
	bplus_tree_delete(tree, 5);
	bplus_tree_delete(tree,6);
	bplus_tree_delete(tree, 7);
	bplus_tree_delete(tree, 8);
	bplus_tree_delete(tree, 9);
	bplus_tree_delete(tree, 10);
	bplus_tree_delete(tree, 11);
	bplus_tree_delete(tree, 12);
	bplus_tree_delete(tree, 26);
	bplus_tree_delete(tree, 25);
	bplus_tree_delete(tree, 23);
	bplus_tree_printf(tree);

}
相关推荐
aichitang20241 小时前
快乐泛函每一天!内积空间
c++·python·数学·算法·机器学习·ai·泛函分析
Ravikov1 小时前
工具开发-ESP32程序管理系统 | 具体实现(一)
c++
数据库技术讲堂1 小时前
从 GitOps 到数据库变更:NineData 如何打通 CI/CD 的数据库治理链路
java·数据库·ci/cd
Wang's Blog1 小时前
PostgreSQL笔记33: 索引底层原理、扫描类型与属性体系深度解析
数据库·笔记·postgresql
地衣君1 小时前
从 profile 到 kanban:Hermes 多 Agent 协作解析及示例
网络·数据库·tcp/ip
是上好佳佳佳呀1 小时前
【深度学习|DAY05】卷积神经网络深度学习笔记
笔记·深度学习·cnn
秋田君1 小时前
【无标题】
数据库·oracle
蒸蒸yyyyzwd1 小时前
cpp 选手备战秋招学习笔记 day11
redis·笔记·求职招聘
Logintern091 小时前
数据库分区表的索引实际上包含本地索引和全局索引两种类型
数据库·分区索引