B树代码实现

B树

时间复杂度: O(logn)

 

B树基本特征:

在这里插入图片描述

 

在这里插入图片描述

 

目录

一、创建

二、查找

三、分裂

四、插入

五、删除

六、完整代码

 

一、创建

思路

B树的创建,包含关键码个数,父亲结点,孩子指针数组并且第一个元素为空,关键码数组,这两个数组索引位置很巧妙,在下面的查找操作中十分合适,下图为这两个数组的抽象显示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2QR8Ph9e-1667360278351)(B树.assets/image-20221027214441377.png)]

 

代码实现
#define MAXM 10 //定义B树最大阶数
const int m = 4;//设定B树的阶数
const int Max = m - 1;//设定B树的最大关键码数
const int Min = (m - 1) / 2;//设定B数的最小关键码数等于(阶数-1)向下取整
typedef int KeyType;

typedef struct node
{
	int keynum;					//关键码个数
	KeyType key[MAXM];			//关键码数组,key[0]不使用
	struct node* parent;		//双亲结点指针
	struct node* ptr[MAXM];		//孩子结点指针数组
}BTNode, *BTree;
	
typedef struct					//B树查找结果类型
{
	BTree pt;                 //查找到的超级结点
	int i;                      //所查元素在超级结点数组中的索引
	int flag;                   //查找是否成功的标志
}Result;

typedef struct LNode			//链表和链表结点类型
{
	BTree data;					//数据域
	struct LNode* next;			//指针域
}LNode, *LinkList;

typedef enum status
{
	TRUE,
	FALSE,
	OK,
	ERROR,
	SOLVEOVERFLOW,
	EMPTY
}Status;

返回目录

 

二、查找

思路

当查询一个关键码时,如果查到,就返回那个结点和所在索引位置;如果没有查到,就返回应该插入的位置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wOvj14to-1667360278351)(B树.assets/image-20221027230704355.png)]

 

代码实现
int SearchBTNode(BTNode* p, KeyType k)
{
	int i = 0;
	for (i = 0; i < p->keynum && p->key[i + 1] <= k; i++) {}
	return i;
}

Result SearchBTree(BTree t, KeyType k)
{
	Result r;
	BTNode* p = t;								//初始化结点p和q,p指向待查结点,q指向p的父亲
	BTNode* q = nullptr;
	int i = 0;
	int found_flag = 0;
	while (p && 0 == found_flag)
	{
		i = SearchBTNode(p, k);
		if (i > 0 && p->key[i] == k)
		{
			found_flag = 1;
		}
		else
		{
			q = p;
			p = p->ptr[i];
		}
	}
	if (1 == found_flag)
	{
		r.pt = p;
		r.i = i;
		r.flag = 1;
	}
	else
	{
		r.pt = q;
		r.i = i;
		r.flag = 0;
	}
	return r;								//返回查找元素的位置或者插入元素的位置
}

返回目录

 

三、分裂

思路

此处分裂原则是,s = (m + 1) / 2 ,将s作为新的父结点,s的左边为它的左孩子,右边为它的右孩子

新分裂出的左孩子⌊(m + 1) / 2⌋ - 1 = ⌈m / 2⌉ - 1

新分裂出的右孩子m - ⌈m / 2⌉ = ⌊m / 2⌋

可见左右孩子都符合m阶B树的条件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uBJyi66I-1667360278352)(B树.assets/image-20221028002612119.png)]

 

代码实现
void SplitBTNode(BTNode*& p, BTNode*& q)
{
	int s = (m + 1) / 2;
	q = (BTNode*)malloc(sizeof(BTNode));
	q->ptr[0] = p->ptr[s];
	for (int i = s + 1; i <= m; i++)
	{
		q->key[i - s] = p->key[i];
		q->ptr[i - s] = p->ptr[i];
	}
	q->parent = p->parent;
	q->keynum = p->keynum - s;
	for (int i = 0; i <= q->keynum; i++)
	{
		if (q->ptr[i])
		{
			q->ptr[i]->parent = q;
		}
	}
	p->keynum = s - 1;
}

void NewRoot(BTNode*& t, KeyType k, BTNode* p, BTNode* q)
{
	t = (BTNode*)malloc(sizeof(BTNode));
	t->keynum = 1;
	t->key[1] = k;
	t->ptr[0] = p;
	t->ptr[1] = q;
	t->parent = nullptr;
	if (p)
	{
		p->parent = t;
	}
	if (q)
	{
		q->parent = t;
	}
    t->parent=NULL;
}

返回目录

 

四、插入

思路

插入共有三种情况:

  1. 当前树为空,就新生成一个新的树,左右孩子为空

  2. 关键码插入后,超级结点没有超过最大关键码数

  3. 关键码插入后,超级结点超过了最大关键码数,首先分裂,将中间结点插入到它们的父亲结点,此时父亲结点没有超过最大关键码数

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-44YeTOOy-1667360278352)(B树.assets/image-20221029113113016.png)]

  1. 关键码插入后,超级结点超过了最大关键码数,首先分裂,将中间结点插入到它们的父亲结点,此时父亲结点超过了最大关键码数,将父亲结点再进行分裂,可能一直分裂到根结点,当根结点分裂完毕后,就停止分裂,新生成一个树,将它的左右孩子赋值为分裂后的两颗子树

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-szbB2rBc-1667360278353)(B树.assets/image-20221029113542749.png)]

 

代码实现
void InsertBTree(BTree& t, int i, KeyType k, BTNode* p)
{
	BTNode* q = nullptr;
	int newroot_flag, finished_flag, s;
	KeyType x;
	if (!p)								//树t为空
	{
		NewRoot(t, k, nullptr, nullptr);
	}
	else
	{
		newroot_flag = 0;
		finished_flag = 0;
		x = k;
		while (!newroot_flag && !finished_flag)
		{
			InsertBTNode(p, i, x, q);
			if (p->keynum <= Max)
			{
				finished_flag = 1;
			}
			else
			{
				SplitBTNode(p, q);
				s = (m + 1) / 2;
				x = p->key[s];
				if (p->parent)
				{
					p = p->parent;
					i = SearchBTNode(p, x);
				}
				else
				{
					newroot_flag = 1;
				}
			}
		}
		if (1 == newroot_flag)
		{
			NewRoot(t, x, p, q);
		}
	}
}

返回目录

 

五、删除

思路

删除可以分为两种情况:

  1. 当左右兄弟可以借出结点时
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-itAZNoyh-1667360278353)(B树.assets/image-20221029141722019.png)]
  2. 当左右兄弟借不出结点时,就合并结点
    在这里插入图片描述

 

代码实现
void Remove(BTNode* p, int i)
{
	for (int j = i + 1; j <= p->keynum; j++)
	{
		p->key[j - 1] = p->key[j];
		p->ptr[j - 1] = p->ptr[j];
	}
	p->keynum--;
}

void MoveRight(BTNode* p, int i)
{
	BTNode* q = p->ptr[i];
	BTNode* aq = p->ptr[i - 1];
	for (int j = q->keynum; j > 0; j++)
	{
		q->key[j + 1] = q->key[j];
		q->ptr[j + 1] = q->ptr[j];
	}
	q->ptr[1] = q->ptr[0];
	q->key[1] = p->key[i];
	q->keynum++;
	p->key[i] = aq->key[aq->keynum];
	p->ptr[i]->ptr[0] = aq->ptr[aq->keynum];
	aq->keynum--;
}

void MoveLeft(BTNode* p, int i)
{
	BTNode* q = p->ptr[i];
	BTNode* aq = p->ptr[i - 1];
	aq->key[aq->keynum + 1] = p->key[i];
	aq->ptr[aq->keynum + 1] = q->ptr[0];
	aq->keynum++;
	p->key[i] = q->key[1];
	q->ptr[0] = q->ptr[1];
	for (int i = 2; i <= q->keynum; i++)
	{
		q->key[i - 1] = q->key[i];
		q->ptr[i - 1] = q->ptr[i];
	}
	q->keynum--;
}

void Combine(BTNode* p, int i)
{
	BTNode* q = p->ptr[i];
	BTNode* aq = p->ptr[i - 1];
	aq->keynum++;
	q->key[q->keynum] = p->key[i];
	q->ptr[q->keynum] = q->ptr[0];
	for (int j = 1; j <= q->keynum; j++)
	{
		aq->keynum++;
		aq->key[aq->keynum] = q->key[j];
		aq->ptr[aq->keynum] = q->ptr[j];
	}

	for (int j = i; j < p->keynum; j++)
	{
		p->key[j] = p->key[j + 1];
		p->ptr[j] = p->ptr[j + 1];
	}
	p->keynum--;
	free(q);
}

void AdjustBTree(BTNode* p, int i)
{
	if (0 == i)
	{
		if (p->ptr[1]->keynum > Min)
		{
			MoveLeft(p, 1);
		}
		else
		{
			Combine(p, 1);
		}
	}
	else if (p->keynum == i)
	{
		if (p->ptr[i - 1]->keynum > Min)
		{
			MoveRight(p, i);
		}
		else
		{
			Combine(p, i);
		}
	}
	else if (p->ptr[i - 1]->keynum > Min)
	{
		MoveRight(p, i);
	}
	else if (p->ptr[i + 1]->keynum > Min)
	{
		MoveLeft(p, i + 1);
	}
	else
	{
		Combine(p, i);
	}
}

int FindBTNode(BTNode* p, KeyType k, int& i)
{
	if (k < p->key[1])
	{
		i = 0;
		return 0;
	}
	else
	{
		i = p->keynum;
		while (k < p->key[i] && i > 1)
		{
			i--;
		}
		if (p->key[i] == k)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
}

void Substitution(BTNode* p, int i)
{
	BTNode* q;
	for (q = p->ptr[i]; q->ptr[0] != nullptr; q = q->ptr[0])
	{
		p->key[i] = q->key[1];
	}
}

int BTNodeDelete(BTNode* p, KeyType k)
{
	int i;
	int found_flag;
	if (!p)
	{
		return 0;
	}
	else
	{
		found_flag = FindBTNode(p, k, i);
		if (1 == found_flag)
		{
			if (p->ptr[i - 1] != nullptr)
			{

			}
			else
			{
				Remove(p, i);
			}
		}
		else
		{
			found_flag = BTNodeDelete(p->ptr[i], k);
		}
		if (p->ptr[i] != nullptr)
		{
			if (p->ptr[i]->keynum < Min)
			{
				AdjustBTree(p, i);
			}
		}
		return found_flag;
	}
}

void BTreeDelete(BTree& t, KeyType k)
{
	BTNode* p;
	int a = BTNodeDelete(t, k);
	if (a == 0)
	{
		cout << "关键码k不在树中!" << endl;
	}
	else if (t->keynum)
	{
		p = t;
		t = t->ptr[0];
		free(p);
	}
}

void DestroyBTree(BTree& t)
{
	BTNode* p = t;
	if (p)
	{
		for (int i = 0; i <= p->keynum; i++)
		{
			DestroyBTree(p->ptr[i]);
		}
		free(p);
	}
	t = nullptr;
}

返回目录

 

六、完整代码

#include <iostream>
#include <cstdlib>
using namespace std;
#define MAXM 10 //定义B树最大阶数
const int m = 4;//设定B树的阶数
const int Max = m - 1;//设定B树的最大关键码数
const int Min = (m - 1) / 2;//设定B数的最小关键码数等于(阶数-1)向下取整
typedef int KeyType;

typedef struct node
{
	int keynum;					//关键码个数
	KeyType key[MAXM];			//关键码数组,key[0]不使用
	struct node* parent;		//双亲结点指针
	struct node* ptr[MAXM];		//孩子结点指针数组
}BTNode, *BTree;
	
typedef struct					//B树查找结果类型
{
	BTree pt;                 //查找到的超级结点
	int i;                      //所查元素在超级结点数组中的索引
	int flag;                   //查找是否成功的标志
}Result;

typedef struct LNode			//链表和链表结点类型
{
	BTree data;					//数据域
	struct LNode* next;			//指针域
}LNode, *LinkList;

typedef enum status
{
	TRUE,
	FALSE,
	OK,
	ERROR,
	SOLVEOVERFLOW,
	EMPTY
}Status;

Status InitBTree(BTree& t);						//初始化B树
int SearchBTNode(BTNode* p, KeyType k);			//在超级结点p中查找关键码k的插入位置i
Result SearchBTree(BTree t, KeyType k);			//在树T上查找关键字k,返回结果(pt,i,tag)
												//如果查找成功,tag = 1,关键码k是指针pt所指
												//结点中的第i个关键字,否则特征值为tag = 0;
//将关键码K和结点q分别插入到p->key[i + 1]和p->ptr[i + 1]中
void InsertBTNode(BTNode*& p, int i, KeyType k, BTNode* q);
//将结点p分裂成两个结点,前一半保留,后一半移入结点q
void SplitBTNode(BTNode*& p, BTNode*& q);
//生成新的根结点t,原p和q为子树指针
void NewRoot(BTNode*& t, KeyType k, BTNode* p, BTNode* q);
/*在树t上结点q的key[i]与key[i+1]之间插入关键字k。若引起
结点过大,则沿双亲链进行必要的结点分裂调整,使t仍是B树*/
void InsertBTree(BTree& t, int i, KeyType k, BTNode* p);
//从p结点删除key[i]和它的孩子指针ptr[i]
void Remove(BTNode* p, int i);
//双亲结点p中的最后一个关键字移入右节点q中,将左结点aq中的最后一个关键字移入双亲结点p中
void MoveRight(BTNode* p, int i);
//双亲结点p中的第一个关键字移入结点aq中,将q中第一个关键字移入双亲结点p中
void MoveLeft(BTNode* p, int i);
//双亲结点p、右节点q合并入左结点aq,并调整双亲结点p中剩余关键字的位置
void Combine(BTNode* p, int i);
//删除结点p中的第i个关键字后,调整B树
void AdjustBTree(BTNode* p, int i);
//反应是否在结点p中找到关键字k
int FindBTNode(BTNode* p, KeyType k, int &i);
//查找被删关键字p->key[i](在非叶子结点中)的替代叶子结点(右子树中值最小的关键字)
void Substitution(BTNode* p, int i);
//在结点p中查找并删除关键字k
int BTNodeDelete(BTNode* p, KeyType k);
//构建删除框架,执行删除操作
void BTreeDelete(BTree& t, KeyType k);
//递归释放B树
void DestroyBTree(BTree& t);

Status InitBTree(BTree& t)
{
	t = nullptr;
	return OK;
}

int SearchBTNode(BTNode* p, KeyType k)
{
	int i = 0;
	for (i = 0; i < p->keynum && p->key[i + 1] <= k; i++) {}
	return i;
}

Result SearchBTree(BTree t, KeyType k)
{
	Result r;
	BTNode* p = t;								//初始化结点p和q,p指向待查结点,q指向p的父亲
	BTNode* q = nullptr;
	int i = 0;
	int found_flag = 0;
	while (p && 0 == found_flag)
	{
		i = SearchBTNode(p, k);
		if (i > 0 && p->key[i] == k)
		{
			found_flag = 1;
		}
		else
		{
			q = p;
			p = p->ptr[i];
		}
	}
	if (1 == found_flag)
	{
		r.pt = p;
		r.i = i;
		r.flag = 1;
	}
	else
	{
		r.pt = q;
		r.i = i;
		r.flag = 0;
	}
	return r;								//返回查找元素的位置或者插入元素的位置
}

void InsertBTNode(BTNode*& p, int i, KeyType k, BTNode* q)
{
	for (int j = p->keynum; j > i; j--)
	{
		p->key[j + 1] = p->key[j];
		p->ptr[j + 1] = p->ptr[j];
	}
	p->key[i + 1] = k;
	p->ptr[i + 1] = q;
	if (q)
	{
		q->parent = p;
	}
	p->keynum++;
}

void SplitBTNode(BTNode*& p, BTNode*& q)
{
	int s = (m + 1) / 2;
	q = (BTNode*)malloc(sizeof(BTNode));
	q->ptr[0] = p->ptr[s];
	for (int i = s + 1; i <= m; i++)
	{
		q->key[i - s] = p->key[i];
		q->ptr[i - s] = p->ptr[i];
	}
	q->parent = p->parent;
	q->keynum = p->keynum - s;
	for (int i = 0; i <= q->keynum; i++)
	{
		if (q->ptr[i])
		{
			q->ptr[i]->parent = q;
		}
	}
	p->keynum = s - 1;
}

void NewRoot(BTNode*& t, KeyType k, BTNode* p, BTNode* q)
{
	t = (BTNode*)malloc(sizeof(BTNode));
	t->keynum = 1;
	t->key[1] = k;
	t->ptr[0] = p;
	t->ptr[1] = q;
	t->parent = nullptr;
	if (p)
	{
		p->parent = t;
	}
	if (q)
	{
		q->parent = t;
	}
	t->parent = NULL;
}

void InsertBTree(BTree& t, int i, KeyType k, BTNode* p)
{
	BTNode* q = nullptr;
	int newroot_flag, finished_flag, s;
	KeyType x;
	if (!p)								//树t为空
	{
		NewRoot(t, k, nullptr, nullptr);
	}
	else
	{
		newroot_flag = 0;
		finished_flag = 0;
		x = k;
		while (!newroot_flag && !finished_flag)
		{
			InsertBTNode(p, i, x, q);
			if (p->keynum <= Max)
			{
				finished_flag = 1;
			}
			else
			{
				SplitBTNode(p, q);
				s = (m + 1) / 2;
				x = p->key[s];
				if (p->parent)
				{
					p = p->parent;
					i = SearchBTNode(p, x);
				}
				else
				{
					newroot_flag = 1;
				}
			}
		}
		if (1 == newroot_flag)
		{
			NewRoot(t, x, p, q);
		}
	}
}

void Remove(BTNode* p, int i)
{
	for (int j = i + 1; j <= p->keynum; j++)
	{
		p->key[j - 1] = p->key[j];
		p->ptr[j - 1] = p->ptr[j];
	}
	p->keynum--;
}

void MoveRight(BTNode* p, int i)
{
	BTNode* q = p->ptr[i];
	BTNode* aq = p->ptr[i - 1];
	for (int j = q->keynum; j > 0; j++)
	{
		q->key[j + 1] = q->key[j];
		q->ptr[j + 1] = q->ptr[j];
	}
	q->ptr[1] = q->ptr[0];
	q->key[1] = p->key[i];
	q->keynum++;
	p->key[i] = aq->key[aq->keynum];
	p->ptr[i]->ptr[0] = aq->ptr[aq->keynum];
	aq->keynum--;
}

void MoveLeft(BTNode* p, int i)
{
	BTNode* q = p->ptr[i];
	BTNode* aq = p->ptr[i - 1];
	aq->key[aq->keynum + 1] = p->key[i];
	aq->ptr[aq->keynum + 1] = q->ptr[0];
	aq->keynum++;
	p->key[i] = q->key[1];
	q->ptr[0] = q->ptr[1];
	for (int i = 2; i <= q->keynum; i++)
	{
		q->key[i - 1] = q->key[i];
		q->ptr[i - 1] = q->ptr[i];
	}
	q->keynum--;
}

void Combine(BTNode* p, int i)
{
	BTNode* q = p->ptr[i];
	BTNode* aq = p->ptr[i - 1];
	aq->keynum++;
	q->key[q->keynum] = p->key[i];
	q->ptr[q->keynum] = q->ptr[0];
	for (int j = 1; j <= q->keynum; j++)
	{
		aq->keynum++;
		aq->key[aq->keynum] = q->key[j];
		aq->ptr[aq->keynum] = q->ptr[j];
	}

	for (int j = i; j < p->keynum; j++)
	{
		p->key[j] = p->key[j + 1];
		p->ptr[j] = p->ptr[j + 1];
	}
	p->keynum--;
	free(q);
}

void AdjustBTree(BTNode* p, int i)
{
	if (0 == i)
	{
		if (p->ptr[1]->keynum > Min)
		{
			MoveLeft(p, 1);
		}
		else
		{
			Combine(p, 1);
		}
	}
	else if (p->keynum == i)
	{
		if (p->ptr[i - 1]->keynum > Min)
		{
			MoveRight(p, i);
		}
		else
		{
			Combine(p, i);
		}
	}
	else if (p->ptr[i - 1]->keynum > Min)
	{
		MoveRight(p, i);
	}
	else if (p->ptr[i + 1]->keynum > Min)
	{
		MoveLeft(p, i + 1);
	}
	else
	{
		Combine(p, i);
	}
}

int FindBTNode(BTNode* p, KeyType k, int& i)
{
	if (k < p->key[1])
	{
		i = 0;
		return 0;
	}
	else
	{
		i = p->keynum;
		while (k < p->key[i] && i > 1)
		{
			i--;
		}
		if (p->key[i] == k)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
}

void Substitution(BTNode* p, int i)
{
	BTNode* q;
	for (q = p->ptr[i]; q->ptr[0] != nullptr; q = q->ptr[0])
	{
		p->key[i] = q->key[1];
	}
}

int BTNodeDelete(BTNode* p, KeyType k)
{
	int i;
	int found_flag;
	if (!p)
	{
		return 0;
	}
	else
	{
		found_flag = FindBTNode(p, k, i);
		if (1 == found_flag)
		{
			if (p->ptr[i - 1] != nullptr)
			{

			}
			else
			{
				Remove(p, i);
			}
		}
		else
		{
			found_flag = BTNodeDelete(p->ptr[i], k);
		}
		if (p->ptr[i] != nullptr)
		{
			if (p->ptr[i]->keynum < Min)
			{
				AdjustBTree(p, i);
			}
		}
		return found_flag;
	}
}

void BTreeDelete(BTree& t, KeyType k)
{
	BTNode* p;
	int a = BTNodeDelete(t, k);
	if (a == 0)
	{
		cout << "关键码k不在树中!" << endl;
	}
	else if (t->keynum)
	{
		p = t;
		t = t->ptr[0];
		free(p);
	}
}

void DestroyBTree(BTree& t)
{
	BTNode* p = t;
	if (p)
	{
		for (int i = 0; i <= p->keynum; i++)
		{
			DestroyBTree(p->ptr[i]);
		}
		free(p);
	}
	t = nullptr;
}

返回目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值