算法 B树

B- 树的概念

  • B树就是B-Tree,B-Tree的定义:
  1. 根节点子女数为[2,m];
  2. 除根节点和叶结点以外的所有分支结点至少有[ [m/2] , m]个子女;
  3. 所有的叶结点都位于同一层;
  4. m 阶 B-Tree 的节点结构如下:n, S0, (K1,S1), (K2,S2), … ,(Kn,Sn)
    其中 Si 是指向子树的指针,0 <= i <= n < m;Ki是关键码,1 <= i <= n< m;Ki < Ki+1,1 <= i < n
    • 在子树Si中所有的关键码都小于Ki+1,且大于Ki,0 < i < n
    • 在子树Sn中所有的关机拿吗都大于Kn
    • 在子树S0中的所有关键码都小于K1
    • 子树Si也是m路搜索树,0 <= i <= n

事实上,在B数的每个结点中还包含一组指针D[m],指向实际对象的存放地址
K[i] 与 D[i] (1 <= i <= n < m)形成一个索引项(K[i],D[i]),通过K[i]可找到某个对象的存储地址D[i]
在这里插入图片描述

B- 树的定义

#define M 5
#define MAXSIZE (M-1)  //MAX ELEM  4
#define	MINSIZE (M/2)  //MIN ELEM  2
typedef char KeyType;
typedef struct {}Record;
typedef struct
{
	KeyType key;
	Record* recptr;
}ElemType;   //key_value

typedef struct BNode
{
	int num;
	struct BNode* parent;
	ElemType data[M + 1];
	struct BNode* sub[M + 1];
};
typedef struct
{
	struct BNode* root;
	int cursize;
}BTree;
typedef struct
{
	struct BNode* ptr;
	int index;
	bool tag;
}Result;

B- 树的插入

#define M 5
#define MAXSIZE (M-1)  //MAX ELEM  4
#define	MINSIZE (M/2)  //MIN ELEM  2 3 4
typedef char KeyType;
typedef struct {}Record;
typedef struct
{
	KeyType key;
	Record* recptr;
}ElemType;   //key_value

typedef struct BNode
{
	int num;
	struct BNode* parent;
	ElemType data[M + 1];
	struct BNode* sub[M + 1];
};
typedef struct
{
	struct BNode* root;
	int cursize;
}BTree;
typedef struct
{
	struct BNode* pnode;  
	int index;
	bool tag;  //返回false 则需要在index位置插入
}Result; //返回结构

BNode* Buynode()
{
	BNode* s = (BNode*)malloc(sizeof(BNode));
	if (nullptr == s) exit(1);
	memset(s, 0, sizeof(BNode));
	return s;
}

void Init_BTree(BTree& tree)
{
	tree.root = nullptr;
	tree.cursize = 0;
}

Result FindKey(BTree &tree,KeyType kx)
{
	Result res = { nullptr,-1,false };
	struct BNode* p = tree.root;
	while (p != nullptr)
	{
		p->data[0].key = kx;  //放置哨兵位
		int i = p->num;
		while (i >= 0 && kx < p->data[i].key)
		{
			--i;
		}
		res.pnode = p;
		res.index = i;
		if (i > 0 && kx == p->data[i].key)
		{
			res.tag = true;
			break;
		}
		p = p->sub[i]; //下面结点寻找
	}
	return res;
	//找不到则将该节点插入在res记录的pnode index+1 的位置
}

BNode* MakeRoot(const ElemType& item, BNode* left, BNode* right)
{
	BNode* s = Buynode();
	s->num = 1;
	s->parent = nullptr;
	s->data[1] = item;
	s->sub[0] = left;
	if (left != nullptr) left->parent = s;
	s->sub[1] = right;
	if (right != nullptr)  right->parent = s;

	return s;
}

void Insert_Item(BNode* ptr, int pos, const ElemType& item, BNode* right)
{
	for (int i = ptr->num; i > pos; --i)
	{
		ptr->data[i + 1] = ptr->data[i];  //结点移动
		ptr->sub[i + 1] = ptr->sub[i];    //叶子移动
	}
	ptr->data[pos + 1] = item;   //插入数据
	ptr->sub[pos + 1] = right;   //插入叶子
	ptr->num += 1;
}
ElemType Move_Item(BNode* s, BNode* ptr, int pos) //ptr -> s
{										//MINSIZE
	for (int i = 0, j = pos + 1; j <= ptr->num; ++i, ++j)
	{
		s->data[i] = ptr->data[j];
		s->sub[i] = ptr->sub[j];
		if (s->sub[i] != nullptr) //移动孩子
		{
			s->sub[i]->parent = s;
		}
	}
	s->num = MINSIZE;
	ptr->num = MAXSIZE - MINSIZE - 1;
	s->parent = ptr->parent;
	return s->data[0]; //返回哨兵 需要将其移动至双亲结点
}
BNode* Splice(BNode* ptr) //分裂
{
	BNode* s = Buynode();
	ElemType item = Move_Item(s, ptr, MINSIZE);
	if (ptr->parent == nullptr)
	{
		return MakeRoot(item, ptr, s); //创建根
	}
	BNode* pa = ptr->parent;
	int pos = pa->num;
	pa->data[0] = item;
	while (pos > 0 && item.key < pa->data[pos].key)
	{
		--pos;
	}
	Insert_Item(pa, pos, item, s); //找到合适位置在双亲结点插入
	if (pa->num > MAXSIZE)
	{
		return Splice(pa);
	}
	else
	{
		return nullptr;
	}
}
bool Insert(BTree& tree, const ElemType& item)
{
	if (tree.root == nullptr)
	{
		tree.root = MakeRoot(item, nullptr, nullptr); //创建root
		tree.cursize = 1;
		return true;
	}
	Result res = FindKey(tree, item.key); //查找插入位置
	if (res.pnode != nullptr && res.tag) return false;
	BNode* ptr = res.pnode;
	int pos = res.index;
	Insert_Item(ptr, pos, item, nullptr); //移动叶子与结点
	if (ptr->num > MAXSIZE) //需要进行分裂
	{
		BNode* newroot = Splice(ptr);  
		if (newroot != nullptr) //不需要
		{
			tree.root = newroot;
		}
	}
	tree.cursize += 1;
	return true;
}

int main()
{
	BTree myt;
	Init_BTree(myt);
	char ch[] = { "wertyuisdfghjkxcvbnm" };
	int i = 0;
	while (ch[i] != '\0')
	{
		ElemType item = { ch[i],nullptr };
		cout << Insert(myt, item);
		++i;
	}
	cout << endl;
}

B- 树遍历

void InOrder(BNode* ptr)
{
	if (ptr == nullptr) return;
	InOrder(ptr->sub[0]);
	for (int i = 1; i <= ptr->num; ++i)
	{
		cout << ptr->data[i].key;
		InOrder(ptr->sub[i]);
	}
}

首先打印sub[0]结点,之后剩下数据与分支都能进行配对,每个分支都要左右两个孩子

B- 树的删除

假设我们需要删除的结点不为叶子结点,则进行数据转移,寻找其直接前驱
在这里插入图片描述
例如 i 的直接前驱 h,对其进行数据转移直接将该叶子结点最后一位进行删除,倘若该前驱的叶子分支大小为 2 MINSIZE,则去寻找后继结点,也就是 j 结点,同样进行替换删除,但是后继结点需要进行数据的移位

再加入,要删除结点的前驱与后继的叶子分支都为 2 MINSIZE,那么则从其左边的叶子结点进行转移(先左后右)
在这里插入图片描述
此时e 所处结点大小小于MINSIZE,则将 d 结点进行下移,并将左边的页结点中的 c 进行上移
在这里插入图片描述
当我们需要删除 8 结点的时候,左兄弟的结点不够右兄弟也不够,则需要进行合并
在这里插入图片描述
在这里插入图片描述
将结点 3 移动至 4 结点分支的0号位置,再整体将其进行左合并(先左后右),并将 9 结点向前移动,以及其孩子结点向前移动,c 结点与孩子也同样的进行移动
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值