算法导论 第18章 B树

本文详细介绍了B树的概念,包括其定义、数据结构、特征及在磁盘操作中的应用。同时,探讨了B树的基本操作,如插入、删除关键字,并提供了相关练习和思考题,旨在帮助读者深入理解B树在辅存系统中的重要作用。
摘要由CSDN通过智能技术生成

一、定义

1、B树

B树是为磁盘或其它直接存取辅助存储设备而设计的一种平衡查找树,主要特点是降低磁盘I/O操作次数。
B树以自然的方式推广二叉查找树。
B树的分支因子由磁盘特性所决定。 

2、B数的数据结构

int n:当前存储在结点x中的关键字数
key[N]:n个关键,以非降序存放
bool leaf;//TRUE:x是叶子;FALSE:x是内结点
node *child[N+1]:只有内结点才有。指向其n+1个孩子的指针。child[1].key <= key[1] <= child[2].key……

3.B树的特征

(1)只有内结点才有指向子女的指针,且child[1].key <= key[1] <= child[2].key……
(2)每个叶结点具有相同的深度
(3)分支因子t>=2
(4)每个非根结点至少有t-1个关键字,如果是内结点,至少有t个子女
(5)每个结点至多有2t-1个关键字,如果是内结点,到多有2t个子女

4.B树上的操作

B-Tree-Search(x, k)
B-Tree-Create(T)
B-Tree-Split-Child(x,i,y)
B-Tree-Insert(T,k)
B-Tree-Insert-Nonfull(x,k)
B-Tree-Delete(T,x)

二、代码

B_Tree.h

#include <iostream>
using namespace std;

#define N 10
int t = 2;
//B树结点结构
struct node
{
	int n;//当前存储在结点x中的关键字数
	char key[N];//n个关键字,以非降序存放
	bool leaf;//TRUE:x是叶子;FALSE:x是内结点
	node *child[N+1];//指向其n+1个孩子的指针
	//构造函数
	node(int num, bool IsLeaf):n(num),leaf(IsLeaf){}
	//磁盘读写操作
	void Disk_Read(){}
	void Disk_Write(){}
};
//B树结构
class B_Tree
{
public:
	//指向根结点
	node *root;
	B_Tree():root(NULL){}
	
	//从以x为根结点的树中寻找关键字为k的结点,若找到,将结果存入y中,返回其是第几个关键字
	int B_Tree_Search(node *x, char k, node&y);
	//构造一棵带树结点的空树
	void B_Tree_Create();
	//分裂,把y分裂为两个结点,选择其中一个关键字插入到x中的第i个位置
	void B_Tree_Split_Child(node *x, int i, node *y);
	//将关键字k插入到一个未满的结点x中
	void B_Tree_Insert_Nonfull(node *x, char k);
	//向T中插入关键字k
	void B_Tree_Insert(char k);
	//删除T树中关键字为k的结点,由于是递归方法,当前处理的是x结点
	void B_Tree_Delete(node *x, char k);
	//按关键字从小到大输出结点
	void Print(node *n);
};

//从以x为根结点的树中寻找关键字为k的结点,若找到,将结果存入y中,返回其是第几个关键字
int B_Tree::B_Tree_Search(node *x, char k, node&y)
{
	int i = 1;
	//找到第一个关键字不大于k的i
	while(i < x->n && k > x->key[i])
		i++;
	//若key[i] = k,则找到了
	if(i <= x->n && k == x->key[i])
	{
		//将结果存入y中
		y = *x;
		//返回其是第几个关键字
		return i;
	}
	//若没找到,则返回空
	if(x->leaf)
	{
	//	&y = NULL;
		return 0;
	}
	//若还有子树可以找,则递归查找第i个子树
	x->child[i]->Disk_Read();
	return B_Tree_Search(x->child[i], k, y);
}
//构造一棵带树结点的空树
void B_Tree::B_Tree_Create()
{
	//生成一个根结点
	//初始时,根结点为叶子结点,根结点中没有关键字
	root = new node(0, true);
	root->Disk_Write();
}
//分裂,把y分裂为两个结点,选择其中一个关键字插入到x中的第i个位置
void B_Tree::B_Tree_Split_Child(node *x, int i, node *y)
{
	int j;
	//生成一个新结点z
	//要把y分裂为y和z,因此z的叶子属性与y相同
	//分裂前y有2t-1个关键字,分裂后前t-1个属于y,后t-1个属于z,中间第t个属于x
	node *z = new node(t-1, y->leaf);
	y->n = t - 1;
	//后t-1个关键字依次复制给z
	for(j = 1; j < t; j++)
		z->key[j] = y->key[j+t];
	//如果有孩子,孩子也要复制过去,原来有2t个子树,前t个属于y,后t个属于z
	if(y->leaf == false)
	{
		for(j = 1; j <= t; j++)
			z->child[j] = y->child[j+t];
	}
	//使z作为x的第i+1个孩子(y已经是x的第i个孩子)
	for(j = x->n+1; j > i; j--)
		x->child[j+1] = x->child[j];
	x->child[i+1] = z;
	//把y中第t个关键字插入到x的第i个位置
	for(j = x->n; j >= i; j--)
		x->key[j+1] = x->key[j];
	x->key[i] = y->key[t];
	//x的关键字+1
	x->n++;
	y->Disk_Write();
	z->Disk_Write();
	x->Disk_Write();
}
//将关键字k插入到一个未满的结点x中
void B_Tree::B_Tree_Insert_Nonfull(node *x, char k)
{
	int i = x->n;
	//若x是叶子结点
	if(x->leaf)
	{
		//找到该插入的位置
		while(i >= 1 && k < x->key[i])
		{
			x->key[i+1] = x->key[i];
			i--;
		}
		//插入关键字k
		x->key[i+1] = k;
		x->n++;
		x->Disk_Write();
	}
	//若不是叶子结点
	else
	{
		//找到该插入的位置
		while(i >= 1 && k < x->key[i])
			i--;
		i++;
		//读取其孩子,将关键字插入到它的孩子中,分两种情况
		x->child[i]->Disk_Read();
		//孩子已满
		if(x->child[i]->n == 2 * t - 1)
		{
			//对孩子执行分裂操作,分裂后,孩子不变为不满
			B_Tree_Split_Child(x, i, x->child[i]);
			if(k > x->key[i])
				i++;
		}
		//孩子不满,直接对孩子进行插入操作
		B_Tree_Insert_Nonfull(x->child[i], k);
	}
}
//向T中插入关键字k
void B_Tree::B_Tree_Insert(char k)
{
	node *r = root, *s;
	//若根结点已满
	if(r->n == 2*t-1)
	{
		//申请一个新的结点,将新的结点作为根结点
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 17
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值