B+树插入删除实现(c++)

本文介绍了B+树的性质,采用子树比关键字数量多一个的实现方式,阐述了其插入删除操作与B树相似但需注意叶子链表调整。还对比了B树与B+树的区别,如B+树只在叶子节点保存数据,范围搜索效率高,单个数据查询B树更快。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

B+树性质


B+树
B+树的实现有多种,有关键字和子树个数相等的,还有向B树一样的子树比关键字数量多一个。
这里使用第二种方式。
1.树的每个节点最多有M个子树
2.根节点至少有两个子树(如果不是叶子节点)
3.除根节点之外的所有非叶子节点,至少有M/2个子树(向上取整)
4.所有非叶子节点包括keycount,child[0],key[1],child[1],key[2]..key[keycount],child[keycount]
5.节点包括内部节点和叶子节点,B+树的节点信息都保存在叶子节点中,内部节点不保存数据
6.每个叶子节点都保存着它相邻兄弟节点的指针。
keycount为该节点包含的关键字数量
key为关键字
child为子树,子树插在各个关键字之间,所以比key多一个

B+树的插入删除与B树十分相似,只是在操作时要注意对叶子链表的next和头指针进行调整,另外,在插入删除中的分裂、合并过程中,对于叶子节点与非叶子节点的处理稍有不同。

插入删除操作与B树基本一致:

https://blog.csdn.net/yunshuipiao123/article/details/88548622

下面直接贴出代码,如有错误之处,希望大家不吝赐教~


#ifndef _BPLUS_TREE_H
#define _BPLUS_TREE_H
#include<iostream>
#include<string>
#include<queue>
#include<algorithm>
using namespace std;
/*
B+树
B+树的实现有多种,有关键字和子树个数相等的,还有向B树一样的子树比关键字数量多一个。
这里使用第二种方式。
1.树的每个节点最多有M个子树
2.根节点至少有两个子树(如果不是叶子节点)
3.除根节点之外的所有非叶子节点,至少有M/2个子树(向上取整)
4.所有非叶子节点包括keycount,child[0],key[1],child[1],key[2]..key[keycount],child[keycount]
5.节点包括内部节点和叶子节点,B+树的节点信息都保存在叶子节点中,内部节点不保存数据
6.每个叶子节点都保存着它相邻兄弟节点的指针。
keycount为该节点包含的关键字数量
key为关键字
child为子树,子树插在各个关键字之间,所以比key多一个
*/
class MyBPlusTree
{
private:
	enum {
		M = 5,KEY_START = 1,KEY_MIN = (M + 1) / 2 - 1,
		KEY_MAX = M - 1,CHILD_MIN = KEY_MIN + 1,CHILD_MAX = KEY_MAX + 1
	};
	typedef struct Node
	{
		bool	leaf;			//是否为叶子节点(其实可以不需要这个字段的,可以通过对子树第0个位置是否为空判断叶子节点)
		int		keys[M + 1];	//key下标1..M
		Node*	childs[M + 1];	//子树0..M
		int		keycount;
		Node*	parent;
		Node*	next;
		Node(bool l = true,Node* p = nullptr, Node* n = nullptr )
			:leaf(l),parent(p),next(n),keycount(0){}
	}BPlusTree;
	typedef struct Result
	{
		Node*	node;
		int		keypos;
		Result(Node* n = nullptr,int k = 1):node(n),keypos(k){}
	}SearchInsertResult,SearchEraseResult;
	BPlusTree*		root;		//根节点
	BPlusTree*		start;		//链式叶子节点的头结点
	bool			release;
public:
	MyBPlusTree():root(0),start(0),release(false){}
	~MyBPlusTree(){
		this->destroy();
	}
private:
	int		find_keypos(int key,Node* node);
	bool	find_insert(int key,SearchInsertResult* res);
	Node*	find_last_node(Node* node);
	void	_insert(int key,int keypos,Node* dst,Node* child);
	void	split(Node* node,Node*& child);
	void	_destroy(Node* root);
	bool	find_delete(int key,SearchEraseResult* res);
	int		get_childpos(Node* parent,Node* child);
	Node*	combine(Node* node,Node* brother,Node* parent,int parentKeyPos);
public:
	bool	insert(int key);
	bool	erase(int key);
	bool	destroy();
	bool	layer_print();
	bool	leaf_print();
};

int		MyBPlusTree::find_keypos(int key,Node* node)
{
	int pos = KEY_START;
	for(;pos <= node->keycount && node->keys[pos] < key; pos++);
	return pos;
}

bool	MyBPlusTree::find_insert(int key,SearchInsertResult* res)
{
	if(root == nullptr)
		return false;
	auto ptmp = root;
	int pos = 0;
	while(!ptmp->leaf)
	{
		pos = this->find_keypos(key,ptmp);
		ptmp = ptmp->childs[pos - 1];
	}
	pos = this->find_keypos(key,ptmp);	//在叶子中找到合适位置
	if(res){
		res->node = ptmp;
		res->keypos = pos;
	}
	return true;
}

void	MyBPlusTree::_insert(int key,int keypos,Node* dst,Node* child)
{
	for(int i = dst->keycount + 1; i >= keypos; i--)
	{
		dst->keys[i] = dst->keys[i - 1];
		dst->childs[i] = dst->childs[i - 1];
	}
	dst->keys[keypos] = key;
	dst->childs[keypos - 1] = child;
	if(child)
		child->parent = dst;
	++dst->keycount;
}

void	MyBPlusTree::split(Node* node,Node*& child)
{
	int pos = (M + 1) / 2;
	Node* left = new Node(node->leaf,node->parent);
	left->childs[0] = node->childs[0];
	if(!left->leaf)
		left->childs[0]->parent = left;
	for(int i = KEY_START; i < (left->leaf ? pos + 1:pos); i++){
		left->keys[i] = node->keys[i];
		left->childs[i] = node->childs[i];
		if(!left->leaf){
			left->childs[i]->parent = left;
		}
		++left->keycount;
	}

	node->childs[0] = node->childs[pos];
	for(int i = pos + 1; i <= node->keycount; i++){
		node->keys[i - pos] = node->keys[i];
		node->childs[i - pos] = node->childs[i];
	}
	node->keycount = node->keycount - left->keycount - (left->leaf ? 0 : 1);
	child = left;
}

MyBPlusTree::Node*	MyBPlusTree::find_last_node(Node* node)
{
	if(start == node)
		return start;
	auto ptmp = start;
	while(ptmp)
	{
		if(ptmp->next == node)
			break;
		ptmp = ptmp->next;
	}
	return ptmp;
}

bool	MyBPlusTree::insert(int key)
{
	SearchInsertResult res;
	if(this->find_insert(key,&res) == false){
		root = new Node;
		root->keys[KEY_START] = key;
		root->keycount = 1;
		start = root;
		return true;
	}
	auto node = res.node;
	auto keypos = res.keypos;
	Node* child = nullptr;
	auto mid = key;
	bool finished = false;
	while(node)
	{
		this->_insert(mid,keypos,node,child);
		if(node->keycount > KEY_MAX)
		{
			mid = node->keys[(M + 1) / 2];
			this->split(node,child);
			if(node->parent == nullptr)
			{
				root = new Node(false);
				root->keys[KEY_START] = mid;
				root->childs[0] = child;
				root->childs[1] = node;
				root->keycount = 1;
				child->parent = root;
				node->parent = root;
				finished = true;
				if(child->leaf)
				{
					start = child;
					child->next = node;
				}
			}else{
				if(node && node->leaf){
					auto last = this->find_last_node(node);
					if(last == node){
						start = child;
						child->next = node;
					}else{
						last->next = child;
						child->next = node;
					}
				}
				node = node->parent;
				keypos = this->find_keypos(key,node);
			}
			
			if(finished)
				break;
		}else
			break;
	}

	return true;
}

bool	MyBPlusTree::find_delete(int key,SearchEraseResult* res)
{
	if(root == nullptr)
		return false;
	auto ptmp = root;
	int pos = 0;
	while(!ptmp->leaf)
	{
		pos = this->find_keypos(key,ptmp);
		ptmp = ptmp->childs[pos - 1];
	}
	pos = this->find_keypos(key,ptmp);
	if(ptmp->keys[pos] == key)
	{
		res->node = ptmp;
		res->keypos = pos;
	}else
		return false;
	return true;
}

int		MyBPlusTree::get_childpos(Node* parent,Node* child)
{
	int pos = -1;
	for(int i = 0; i <= parent->keycount; i++){
		if(parent->childs[i] == child){
			pos = i;
			break;
		}
	}
	return pos;
}

MyBPlusTree::Node*	MyBPlusTree::combine(Node* node,Node* brother,Node* parent,int parentKeyPos)
{
	Node* mergedNode = new Node(node->leaf,node->parent);
	int key = parent->keys[parentKeyPos];
	for(int i = parentKeyPos; i < parent->keycount; i++){
		parent->keys[i] = parent->keys[i + 1];
		parent->childs[i] = parent->childs[i + 1];
	}
	--parent->keycount;

	if(!mergedNode->leaf){
		if(node->keycount == 0 || key >= node->keys[node->keycount]){
			//说明key值的排序是node key brother也就是node在brother左侧
			node->keys[node->keycount + 1] = key;
			node->childs[node->keycount + 1] = brother->childs[0];	//注意子树也要移动
			++node->keycount;
		}else{
			brother->keys[brother->keycount + 1] = key;
			brother->childs[brother->keycount + 1] = node->childs[0];
			++brother->keycount;
		}
	}

	if(node->keys[1] > brother->keys[brother->keycount]){
		mergedNode->childs[0] = brother->childs[0];
		for(int i = 1; i <= brother->keycount; i++){
			mergedNode->keys[i] = brother->keys[i];
			mergedNode->childs[i] = brother->childs[i];
			if(!mergedNode->leaf)		//不要忘反指
				mergedNode->childs[i]->parent = mergedNode;
		}
		for(int i = brother->keycount + 1; i <= brother->keycount + node->keycount; i++){
			mergedNode->keys[i] = node->keys[i - brother->keycount];
			mergedNode->childs[i] = node->childs[i - brother->keycount];
			if(!mergedNode->leaf)		//不要忘反指
				mergedNode->childs[i]->parent = mergedNode;
		}

		if(mergedNode->leaf)
		{
			auto last = this->find_last_node(brother);
			if(last == brother){
				start = mergedNode;
			}else{
				last->next = mergedNode;
			}
			mergedNode->next = node->next;
		}
	}else{
		mergedNode->childs[0] = node->childs[0];
		for(int i = 1; i <= node->keycount; i++){
			mergedNode->keys[i] = node->keys[i];
			mergedNode->childs[i] = node->childs[i];
			if(!mergedNode->leaf)		//不要忘反指
				mergedNode->childs[i]->parent = mergedNode;
		}
		for(int i = node->keycount + 1; i <= node->keycount + brother->keycount; i++){
			mergedNode->keys[i] = brother->keys[i - node->keycount];
			mergedNode->childs[i] = brother->childs[i - node->keycount];
			if(!mergedNode->leaf)		//不要忘反指
				mergedNode->childs[i]->parent = mergedNode;
		}

		if(mergedNode->leaf)
		{
			auto last = this->find_last_node(node);
			if(last == node){
				start = mergedNode;
			}else{
				last->next = mergedNode;
			}
			mergedNode->next = brother->next;
		}
	}
	mergedNode->keycount = node->keycount + brother->keycount;
	parent->childs[parentKeyPos - 1] = mergedNode;
	
	
	delete node;
	delete brother;

	return mergedNode;
}

bool	MyBPlusTree::erase(int key)
{
	SearchEraseResult res;
	if(this->find_delete(key,&res) == false)
		return false;
	auto node = res.node;
	int  keypos = res.keypos;
	//找到的节点都是叶子节点,直接移动删除
	for(int i = keypos; i < node->keycount; i++)
	{
		node->keys[i] = node->keys[i + 1];
	}
	--node->keycount;
	while(1)
	{
		if(node->parent == nullptr)
		{
			if(node->keycount == 0)
			{
				if(node->leaf){
					root = nullptr;
				}else{
					root = node->childs[0];
				}
				auto last = this->find_last_node(node);
				if(last == node){
					start = nullptr;
				}else{
					last->next = node->next;
				}
				delete node;
			}
			return true;
		}

		if(node->keycount < KEY_MIN)
		{
			auto parent = node->parent;
			int childpos = this->get_childpos(parent,node);
			if(childpos > 0 && parent->childs[childpos - 1]->keycount > KEY_MIN)
			{//说明左兄弟节点有,多个key值可以向他借
				auto left = parent->childs[childpos - 1];
				for(int i = node->keycount + 1; i >= 1; i--){
					node->keys[i] = node->keys[i - 1];
					node->childs[i] = node->childs[i - 1];
				}
				//------------------与B树操作不同------------
				if(node->leaf)		//这里与B树不同,B+树对叶子节点和非叶子节点的删除有一点差别
					node->keys[1] = left->keys[left->keycount];
				else
					node->keys[1] = parent->keys[childpos];
				++node->keycount;
				//------------------------------------------

				node->childs[0] = left->childs[left->keycount];	//携带的child
				if(!node->leaf)
					node->childs[0]->parent = node;

				parent->keys[childpos] = left->keys[left->keycount - 1];
				--left->keycount;
				return true;
			}else if(childpos < parent->keycount && parent->childs[childpos + 1]->keycount > KEY_MIN)
			{//左兄弟借不到,右兄弟有多的,向他借
				auto right = parent->childs[childpos + 1];
				//------------------与B树操作不同------------
				if(node->leaf)
					node->keys[node->keycount + 1] = right->keys[KEY_START];
				else
					node->keys[node->keycount + 1] = parent->keys[childpos + 1];
				++node->keycount;
				//------------------------------------------

				parent->keys[childpos + 1] = right->keys[KEY_START + 1];

				
				node->childs[node->keycount] = right->childs[0];//携带的child
				if(!node->leaf)
					node->childs[0]->parent = node;

				for(int i = 0; i < right->keycount; i++){
					right->keys[i] = right->keys[i + 1];
					right->childs[i] = right->childs[i + 1];
				}

				--right->keycount;
				return true;
			}else
			{//借不到,最后一招,合并
				Node* mergedNode = nullptr;
				if(childpos > 0)
				{//有左兄弟,与他和父节点中的一个key合并
					auto left = parent->childs[childpos - 1];
					mergedNode = this->combine(node,left,parent,childpos);
				}else if(childpos < CHILD_MAX - 1)
				{//右兄弟
					auto right = parent->childs[childpos + 1];
					mergedNode = this->combine(node,right,parent,childpos + 1);
				}
				node = mergedNode->parent;	//上溯
			}
		}else
			return true;
	}
	return true;
}

bool	MyBPlusTree::layer_print()
{
	if(root == nullptr)
		return false;
	Node* ptmp = root;
	Node* node = nullptr;
	queue<Node*> Q;
	Q.push(ptmp);
	while(!Q.empty())
	{
		node = Q.front();
		Q.pop();
		node->leaf ? 0 : Q.push(node->childs[0]);
		for(int i = KEY_START; i <= node->keycount; i++){
			cout<<node->keys[i]<<" ";
			if(node->leaf == false)
				Q.push(node->childs[i]);
		}
	}
	cout<<endl;
	return true;
}

void	MyBPlusTree::_destroy(Node* root)
{
	if(root == nullptr)
		return;
	if(root->leaf == false)
		for(int i = root->keycount; i >= 0; i--)
			this->_destroy(root->childs[i]);

	delete root;
	root = nullptr;
}

bool	MyBPlusTree::destroy()
{
	if(this->release == false)
	{
		this->_destroy(root);
		this->release = true;
	}
	return true;
}

bool	MyBPlusTree::leaf_print()
{
	auto ptmp = start;
	while(ptmp)
	{
		for(int i = KEY_START; i <= ptmp->keycount; i++)
			cout<<ptmp->keys[i]<<" ";
		ptmp = ptmp->next;
	}
	cout<<endl;
	return true;
}
#endif // !_B+_TREE_H

B树与B+树的区别:

B树在每个节点中都保存了数据,而B+树只在叶子节点中保存了数据信息,非叶子节点只是被用于索引。

由于B+树的非叶子节点没有保存实际数据,所以在大小相同的节点中B+树能保存的键值更多。

由于B+树的保存数据的叶子节点都使用指针相连,所以在进行范围搜索时B+树的效率更高。

进行单个数据进行查找时,由于B+树的数据信息都保存在叶子中,所以每次都需要遍历都底,而B树则可能在根节点就找到了,因此在进行单个数据查询时,B树还是要更快一些。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值