B树的插入

本文介绍了B树的插入过程,包括B树的性质、结构体定义以及插入步骤。通过判断关键字是否存在、找到插入位置、节点分裂等操作,详细阐述了如何在B树中正确插入数据。

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

今天,我们来分享一下B树的插入。

B树是一种适合外查找且平衡的多叉树,下面简单介绍一下N阶B树(N叉树)的性质。

a. 根结点至少有两个孩子。(根结点至少有一个关键字,一个关键字有两个孩子

b. 每个非根结点有[N/2, N]个孩子。(最多N个孩子,保证该树是N叉树

c. 每个非根结点有[N/2-1, N-1]个关键字,且以升序排列。(孩子比关键字多1

d. 关键字_key[i]和_key[i+1]之间的孩子节点_sub[i+1]的值介于_key[i]、_key[i+1]之间.( _key[i]<_sub[i+1]<_key[i+1])

 

注:我们习惯吧_sub[i+1]看做_key[i]的右孩子,并且_sub[i+1]看做是_key[i+1]的左孩子。

每一个节点不能有N个关键字,到达N个关键字,节点会分裂为3个节点:中间下标的关键字和孩子构成一个节点(父亲),中间下标以左的关键字和孩子为一个节点(左孩子),中间下标以右的关键字和孩子为一个节点(左孩子)。当N为奇数,左右节点个数相同((N-1)/2 = N/2);当N为偶数, 左右节点个数不相同,必定会一个个数为N/2-1,另一个为N/2,所以定义每个非根结点有[N/2-1, N-1]个关键字。

 

由上面的性质定义B树的结构体如下:

template<class K, size_t N = 3>

struct BTreeNode

{

K _key[N];//关键字个数

BTreeNode<K, N> * _sub[N+1];//孩子个数

BTreeNode<K, N> * _parent;//父亲节点

int _size;//节点关键字个数

};

 

B树插入思路:

1》 判断带插入的数key是否存在。

a.已存在,返回key值出现的结点以及key值出现的位置,不在继续插入。

b.不存在,返回满足插入的叶子结点,继续2》。

2》 得到满足插入的(叶子)结点,找到准确的插入位置,插入key和右孩子(一开始没有右孩子默认为NULL),并且增加节点关键字个数,即_size++.

3》 判断此时节点的关键字个数是否达到N,需要分裂。

a.关键字个数未达到N,不用分裂,不用再继续。

b.关键字个数达到N,需要分裂,继续4》。

4》 找到当前节点的关键字中间下标,把中间下标以右的关键字包括孩子复制到新节点中。

5》 判断当前节点是否为根结点(根结点的父亲节点为空)。

a.是根结点,生成新根节点,把中间下标的关键字和右孩子放入新根节点中。新节点的左孩子为当前节点,右孩子为新节点,完成分裂,不再继续。

b.不是根节点,把中间下标的关键字和右孩子插入当前节点的的父亲节点中。新节点的左孩子为当前节点,右孩子为新节点。继续2》。

 

注:关于B树,其实没有AVL树麻烦,只是细节繁琐,易出错,写的时候必须注意。

 

代码如下:

 

BTree.h
#pragma once

template<class K, size_t M = 3>
struct BTreeNode
{
	K _key[M];
	BTreeNode<K, M> * _sub[M+1];
	BTreeNode<K, M> * _parent;
	int _size;

	BTreeNode()
		:_parent(NULL)
	{
		memset(_sub, 0, sizeof(_sub));
		_size = 0;
	}
};

template<class K, size_t M = 3>
class BTree
{
public:
	typedef BTreeNode<K, M> Node;
	BTree()
		:_root(NULL)
	{
	}

	bool Insert(const K& key)
	{
		if(_root == NULL)
		{
			_root =new Node;
			_root->_key[0] = key;
			_root->_size = 1;
			return true;
		}

		
		//find,找插入位置
pair<Node* , int> s = _find(key);if(s.second >= 0) return false;Node* cur = s.first;Node* sub = NULL;_InsertKey(cur, key, sub);
		//判断是否需要分裂
if(cur->_size < M) return true;//没破坏规则,不需要分裂while(cur->_size >= M){//分裂size_t mid = cur->_size/2;//split();Node* newpoint = new Node;int i=mid+1;for(; i<cur->_size; i++){newpoint->_key[i-mid-1] = cur->_key[i];newpoint->_sub[i-mid-1] = cur->_sub[i];cur->_key[i] = -1;//清楚痕迹,避免引起误会cur->_sub[i] = NULL;//清楚痕迹,避免引起误会if(newpoint->_sub[i-mid-1]){newpoint->_sub[i-mid-1]->_parent = newpoint;}newpoint->_size += 1;}newpoint->_sub[i-mid-1] = cur->_sub[i];cur->_sub[i] = NULL;//清楚痕迹,避免引起误会cur->_size = cur->_size - newpoint->_size -1;if(cur->_parent == NULL){_root = new Node;_root->_key[0] = cur->_key[mid];cur->_key[mid] = -1;//清楚痕迹,避免引起误会_root->_sub[0] = cur;cur->_parent = _root;_root->_sub[1] = newpoint;newpoint->_parent = _root;_root->_size = 1;return true;}else{K newkey = cur->_key[mid];cur->_key[mid] = -1;//清楚痕迹,避免引起误会cur = cur->_parent;sub = newpoint;_InsertKey(cur ,newkey, sub);}}return true;}void Ordprintf(){Node* cur = _root;_Ordp(cur);cout<<endl;}protected:pair<Node* , int> _find(const K& key){Node* cur = _root;Node* parent = NULL;while(cur){int i=0;for(; i<cur->_size; i++){if(cur->_key[i] == key){return make_pair(cur, i);}else if(cur->_key[i] > key){break;}}parent = cur;cur = cur->_sub[i];}return make_pair(parent , -1);//找到插入的父亲节点}void _InsertKey(Node* cur, const K& key, Node* sub = NULL){assert(cur);for(int i=cur->_size; i>=0; i--){if(cur->_key[i-1] < key){cur->_key[i] = key;cur->_sub[i+1] = sub;if(sub)cur->_sub[i+1]->_parent = cur;break;}else{cur->_key[i] = cur->_key[i-1];cur->_sub[i+1] = cur->_sub[i];if(cur->_sub[i+1])cur->_sub[i+1]->_parent = cur;}}cur->_size ++;return;}void _Ordp(Node* cur){if(cur == NULL) return;int i=0;for(; i<cur->_size; i++){_Ordp(cur->_sub[i]);cout<<cur->_key[i]<<" ";}_Ordp(cur->_sub[i]);}protected:Node* _root;};void TestBTree(){BTree<int> k;int a[] = {53,79,139,49,145,36,101};cout<<"依次插入: ";for(int i=0; i<sizeof(a)/sizeof(a[0]); i++){k.Insert(a[i]);cout<<a[i]<<" ";}cout<<endl<<"中序打印该树: ";k.Ordprintf();}run.cpp#include<iostream>#include<assert.h>using namespace std;#include"BTree.h" int main(){cout<<"B树插入:"<<endl;TestBTree();system("pause");return 0;}


运行界面



分享如上,望共同努力进步!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值