(部分图片源自网络,侵删。部分代码参考《数据结构》(吴伟民))
(可执行文件和代码稍后上传)
一、B-Tree的定义
计算机科学中,B树(英语:B-tree)是一种自平衡的树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除的动作,都在对数时间内完成。B树,概括来说是一个一般化的二叉查找树(binary search tree),可以拥有多于2个子节点。与自平衡二叉查找树不同,B树为系统大块数据的读写操作做了优化。B树减少定位记录时所经历的中间过程,从而加快存取速度。B树这种数据结构可以用来描述外部存储。这种数据结构常被应用在数据库和文件系统的实现上。
m阶B树的特点:
(1)树中每个结点最多含有m棵子树。
(2)若根节点非终端结点,至少有两棵子树
(3)除根节点之外的所有非终端结点,则至少有(m+1)/2棵子树
(4)每个非终端结点:关键字按升序排序,个数大于等于(m-1)/2,小于等于m-1
(5)所有叶子结点出现在同一层,不包含任何信息
例:如图所示这是一棵3阶B树。符合上述特点。
二、基本操作
(一)需要实现的基本操作
1—创建B-Tree
2—销毁B-Tree
3—删除关键字
4—查找关键字
5—中序遍历B-Tree
6—插入关键字
7—先序遍历B-Tree
8—层次遍历B-Tree
(二)操作函数之间的关系
Main.c中的灰色节点都是调用的BTree.C中的操作函数
(三)操作函数功能描述
(1)BTree.c
int SearchBTNode(BTNode *p,KeyType k);
功能:在节点中查找关键字
void SearchBTree(BTree T,KeyType k,Result *r);
功能:在m阶b树上查找关键字void InsertBTree(BTree *T,KeyType k,BTNode *q,int i);
功能:在b树插入关键字
void newRoot(BTree *T,BTNode*p,KeyType x,BTNode *ap);
功能:生成新的根节点
void Insert(BTree *q,int i,KeyType x,BTNode *ap);
功能:插入关键字和新结点指针
void split(BTree *q,int s,BTree *ap);
功能:将节点分裂为两个节点
void Inorder(BTree T);
功能:中序遍历整棵B树,可以快速看出操作是否出错
void preorder(BTree T);
功能:先序遍历整颗B树,以缩进的方式展示B树
void Destroy(BTree *T);
功能:销毁整个B树
void DeleteBTree(BTree *p,int i,BTree *T);
功能:删除B树上某个关键字
void Successor(BTree *p,int i);
功能:交换被删节点和最下层非终端节点最小关键字
void Remove(BTree*p,int i);
功能:从节点p中删除key[i]
void Restore(BTree *p,BTree* T);
功能:调整B树
void MoveLeft(BTree *q,int j);
功能:找左兄弟节点借关键字
void MoveRight(BTree *q,int j);
功能:找右兄弟节点借关键字
void Combine(BTree *q,int j);
功能:和父母节点合并
int Depth(BTree T);
功能:求B树的深度
Status InitQueue(LinkList *L);
功能:初始化队列
LNode* CreateNode(BTree t);
功能:新建一个结点
Status Enqueue(LNode *p,BTree t);
功能:元素q入队列
Status Dequeue(LNode *p,BTree *q);
功能:出队列,并以q返回值
Status IfEmpty(LinkList L);
功能:队列判空
void DestroyQueue(LinkList L);
功能:销毁队列
Status Traverse(BTree t,LinkList L,int newline,int sum);
功能:用队列遍历输出B树
Status PrintBTree(BTree t);
功能:完成建立队列,遍历输出和销毁操作
(2)main.c
void welcome();
功能:主菜单
void CreateBTree();
功能:创建B-Tree
void destroyBTree();
功能:销毁B-Tree
void searchKey();
功能:查找关键字
void delete();
功能:删除关键字
void insertKey();
功能:插入关键字
三、数据存储结构
#define m 3//b树的阶
typedef int KeyType;//关键字类型为数字
typedef struct BTNode
{
int keynum; //结点当前关键字个数
KeyType key[m+1]; //关键字数组,key[0]未用
struct BTNode *parent; //双亲结点指针
struct BTNode *ptr[m+1]; //孩子结点指针数组
}BTNode,*BTree; //b树的结点及指针类型
typedef struct{
BTree pt; //指向找到的结点
int i; //1<=i<=m,在结点中的关键字位置;
int tag; //1查找成功,0查找失败
}Result; //B树查找结果类型
typedef enum status{
TRUE,FALSE,OK,ERROR,OVERFLOW,EMPTY
}Status; //枚举类型返回值
typedef struct LNode{
BTree data; //数据域
struct LNode *next; //指针域
}LNode, *LinkList; //队列和队列结点类型
四、代码实现
(一)BTree.c(此处按照操作函数关系图的最上层函数分别实现)
1.searchBTree
在m阶b树上查找关键字
*参数:*在m阶b树上查找关键字k,用r返回查找结果
*思路:*迭代法。调用查找结点函数。如果找到关键字或者已经查询到叶子节点退出循环。
在循环里需要判断查找结点函数返回的是关键字数组下标还是插入置指针数组下标。
如果是前者就退出循环,是后者则指针下移,直到为叶子结点。
若查找成功则标记tag为1
否则tag=0,若要插入关键字为k的记录,应位于pt结点中的第i-1个和第i个关键字 之间
void SearchBTree(BTree T,KeyType k,Result *r){
int i=0;
int found=0;//用来判断是否查询到该关键字
BTree p=T,q=NULL;
while (p!=NULL&&found==0)
{
i=SearchBTNode(p,k);
if(i<=p->keynum&&p->key[i]==k)found=1;//k在p节点里
else{
//k不在p节点里
q=p;
p=p->ptr[i-1];//指针下移
}
}
if(1==found){
(*r).pt=p;
(*r).i=i;
(*r).tag=1;
printf("查找成功\n");
printf("i=%d",i);
}
else{
//自然退出while循环
(*r).pt=q;
(*r).i=i;
(*r).tag=0;
printf("查找失败\n");
}
}
在节点中查找关键字
参数:p是被查找结点,k是需要查找的关键字
返回值:关键字数组下标或插入位置指针数组下标
思路:i在for循环中递增实现遍历比较该节点关键字,k大于该关键字时i++
情况1:在p中找到k了
情况2:在p中没找到k(找到比k大的数或者整个结点的关键字都遍历完了)
这时候都退出循环,返回i,在外层函数会验证这个i是指关键字数组下标还是插入位 置指针数组下标
int SearchBTNode(BTNode *p,KeyType k){
//查找p所指节点中k的位置
int i=1;
for(;i<=p->keynum&&k>p->key[i];i++);
return i;
}
2.InsertBTree
在b树插入关键字
参数:在T树的q所指节点的i位置插入关键字k
思路:
1.插入一个元素时,首先在B树中是否存在,如果不存在,即比较大小寻找插入位置,在叶子结点处结束,然后在叶子结点中插入该新的元素
如图所示:
2、如果叶子结点空间足够,这里需要向右移动该叶子结点中大于新插入关键字的元素,如果空间满了以致没有足够的空间去添加新的元素,则将该结点进行“分裂”,将一半数量的关键字元素分裂到新的其相邻右结点中,中间关键字元素上移到父结点中(当然,如果父结点空间满了,也同样需要“分裂”操作)
如图所示:
3、当结点中关键元素向右移动了,相关的指针也需要向右移。如果在根结点插入新元素,空间满了,则进行分裂操作,这样原来的根结点中的中间关键字元素向上移动到新的根结点中,因此导致树的高度增加一层
void InsertBTree(BTree *T,KeyType k,BTNode *q,int i){
KeyType x;
int s,finished=0,needNewRoot=0;
BTree ap;
if(NULL==q){
newRoot(&(*T),NULL,k,NULL);
}//生成新的根节点
else{
x=k;
ap=NULL;
while(0==needNewRoot&&0==finished){
Insert(&q,i,x,ap);
if(q->keynum<m ){
finished=1;
}
else
{
s=(m+1)/2;
split(&q,s,&ap);
x=q->key[s];
if(q->parent!=NULL){
q=q->parent;
i=SearchBTNode(q,x);
}
else needNewRoot=1;
}
}
if(1==needNewRoot){
newRoot(&(*T),q,x,ap);
}
}
}
新建根节点
参数:x为新根节点的关键字,t是树原来的根节点,p为新根节点的0号子树,ap为新根节点的1号子树。
思路:只需要新建结点,赋值,调整指针即可
void newRoot(BTree *T,BTNode*p,KeyType x,BTree ap){
(*T)=(BTree)malloc(sizeof(BTNode));
(*T)->keynum = 1;
(*T)->ptr[0]=p;
(*T)->ptr[1]=ap;
(*T)->key[1