B树的定义
B树是一种平衡的多路查找树。
一颗m阶B树,或为空树,或为满足下列特性的m叉树。
(1)树中每个结点最多含有m棵子树;
(2)若根结点不是叶子结点,则至少有两颗子树;
(3)除根之外的所有非终端结点至少有[m/2];
(4)每个非终端结点中包含信息:(n,A0,K1,A1,K2,A2,…,Kn,An)。其中
①Ki(1≤i≤n)为关键字,且关键字按升序排序。
②指针Ai(0≤i≤n)指向子树的根结点。
③关键字的个数n必须满足:[m/2]-1≤n≤m-1
(5)所有的叶子结点都出现在同一层次上,并且不带信息(可以看作是外部节点或查找失败的结点,实际上这些结点不存在,指向这些结点的指针为空)
编程环境与配置
IDE:Dev-C++ 5.11
编程语言:C
程序结构图
B树的抽象数据类型定义
ADT BTree{
数据对象:D是具有相同特性的数据元素的集合
数据关系:R1={<ai-1,ai>|ai-1,ai∈D,i=2,...,n}
R2={<ptr[i-1],ptr[i]>|i=1...,n}
约定a1|key[1]为关键字数组头,an|key[p-<keynum]为关键字数组尾
约定ptr[i]为结点的第i个子树
基本操作:
InitBTree(t)
初始条件:B树已定义
操作结果:初始化B树
SearchBTNode(BTNode *p,KeyType k)
初始条件:结点p已存在
操作结果:在结点p中查找关键字k的插入位置i
Result SearchBTree(BTree t,KeyType k)
初始条件:B树已存在
操作结果:在B树查找关键字k的插入位置,返回查找结果
InsertBTNode(BTNode *&p,int i,KeyType k,BTNode *q)
初始条件:结点p和结点q已存在,0<i<p->keynum
操作结果:将关键字k和结点q分别插入到p->key[i+1]和p->ptr[i+1]中
SplitBTNode(BTNode *&p,BTNode *&q)
初始条件:结点p和结点q已存在
操作结果:将结点p分裂成两个结点,前一半保留,后一半移入结点q
NewRoot(BTNode *&t,KeyType k,BTNode *p,BTNode *q)
初始条件:结点t,p,q已存在
操作结果:生成新的根结点t,原p和q为子树指针
InsertBTree(BTree &t,int i,KeyType k,BTNode *p)
初始条件:结点p和结点t已存在,0<i<p->keynum
操作结果:在B树t中插入关键字k
Remove(BTNode *p,int i)
初始条件:结点p已存在,0<i<p->keynum
操作结果:p结点删除key[i]和它的孩子指针ptr[i]
Substitution(BTNode *p,int i)
初始条件:结点p已存在,0<i<p->keynum
操作结果:查找替代值
MoveRight(BTNode *p,int i)
初始条件:结点p已存在,0<i<p->keynum
操作结果:结点调整右移操作
MoveLeft(BTNode *p,int i)
初始条件:结点p已存在,0<i<p->keynum
操作结果:结点调整左移操作
Combine(BTNode *p,int i)
初始条件:结点p已存在,0<i<p->keynum
操作结果:结点调整合并操作
AdjustBTree(BTNode *p,int i)
初始条件:结点p已存在,0<i<p->keynum
操作结果:B树调整操作
BTNodeDelete(BTNode *p,KeyType k)
初始条件:结点p已存在
操作结果:在结点p中删除关键字k
BTreeDelete(BTree &t,KeyType k)
初始条件:B树t已存在
操作结果:在B树t中删除关键字k
DestroyBTree(BTree &t)
初始条件:B树t已存在
操作结果:递归释放B树
PrintBTree(BTree t)
初始条件:B树t已存在
操作结果:遍历打印B树
}ADT BTree
头文件
定义了需要用到的数据类型,结构体类型,以及所有函数接口;
//==========ADT BTree的表示与实现==========
#ifndef _BTREE_H
#define _BTREE_H
#define MAXM 10 //定义B树的最大的阶数
const int m=4; //设定B树的阶数
const int Max=m-1; //结点的最大关键字数量
const int Min=(m-1)/2; //结点的最小关键字数量
typedef int KeyType; //KeyType为关键字类型
//===============B树存储结构==============
typedef struct node{ //B树和B树结点类型
int keynum; //结点关键字个数
KeyType key[MAXM]; //关键字数组,key[0]不使用
struct node *parent; //双亲结点指针
struct node *ptr[MAXM]; //孩子结点指针数组
}BTNode,*BTree;
typedef struct{ //B树查找结果类型
BTNode *pt; //指向找到的结点
int i; //在结点中的关键字位置;
int tag; //查找成功与否标志
}Result;
typedef struct LNode{ //链表和链表结点类型
BTree data; //数据域
struct LNode *next; //指针域
}LNode, *LinkList;
typedef enum status{ //枚举类型(依次递增)
TRUE,
FALSE,
OK,
ERROR,
OVERFLOW,
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的插入位置为pt结点的第i个*/
void InsertBTNode(BTNode *&p,int i,KeyType k,BTNode *q);
//将关键字k和结点q分别插入到p->key[i+1]和p->ptr[i+1]中
void SplitBTNode(BTNode *&p,BTNode *&q);
//将结点p分裂成两个结点,前一半保留,后一半移入结点q
void NewRoot(BTNode *&t,KeyType k,BTNode *p,BTNode *q);
//生成新的根结点t,原结点p和结点q为子树指针
void InsertBTree(BTree &t,int i,KeyType k,BTNode *p);
/*在树t上结点q的key[i]与key[i+1]之间插入关键字k。若引起
结点过大,则沿双亲链进行必要的结点分裂调整,使t仍是B树*/
void Remove(BTNode *p,int i);
//从p结点删除key[i]和它的孩子指针ptr[i]
void Substitution(BTNode *p,int i);
//查找被删关键字p->key[i](在非叶子结点中)的替代叶子结点(右子树中值最小的关键字)
void MoveRight(BTNode *p,int i);
/*将双亲结点p中的最后一个关键字移入右结点q中
将左结点aq中的最后一个关键字移入双亲结点p中*/
void MoveLeft(BTNode *p,int i);
/*将双亲结点p中的第一个关键字移入结点aq中,
将结点q中的第一个关键字移入双亲结点p中*/
void Combine(BTNode *p,int i);
/*将双亲结点p、右结点q合并入左结点aq,
并调整双亲结点p中的剩余关键字的位置*/
void AdjustBTree(BTNode *p,int i);
//删除结点p中的第i个关键字后,调整B树
int FindBTNode(BTNode *p,KeyType k,int &i);
//反映是否在结点p中是否查找到关键字k
int BTNodeDelete(BTNode *p,KeyType k);
//在结点p中查找并删除关键字k
void BTreeDelete(BTree &t,KeyType k);
//构建删除框架,执行删除操作
void DestroyBTree(BTree &t);
//递归释放B树
Status InitQueue(LinkList &L);
//初始化队列
LNode* CreateNode(BTree t);
//新建一个结点
Status Enqueue(LNode *p,BTree t);
//元素q入队列
Status Dequeue(LNode *p,BTNode *&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);
//输出B树
void Test();
//测试B树功能函数
#endif
B树具体接口实现
2.4.1InitBTree函数
功能:初始化B树
代码实现:
Status InitBTree(BTree &t){
t=NULL;
return OK;
}
2.4.2SearchBTNode函数
功能:在结点p中查找关键字k的插入位置i
代码实现:
int SearchBTNode(BTNode *p,KeyType k){
int i=0;
for(i=0;i<p->keynum&&p->key[i+1]<=k;i++);
return i;
}
2.4.3SearchBTree函数
功能:在树t中查找关键字k,返回查找结果类型
代码实现:
Result SearchBTree(BTree t,KeyType k){
BTNode *p=t,*q=NULL; //初始化结点p和结点q,p指向待查结点,q指向p的双亲
int found_tag=0; //设定查找成功与否标志
int i=0;
Result r; //设定返回的查找结果
while(p!=NULL&&found_tag==0){
i=SearchBTNode(p,k); //在结点p中查找关键字k if(i>0&&p->key[i]==k) //找到待查关键字
found_tag=1; //查找成功
else{ //查找失败
q=p;
p=p->ptr[i];
}
}
if(found_tag==1){ //查找成功
r.pt=p;
r.i=i;
r.tag=1;
}
else{ //查找失败
r.pt=q;
r.i=i;
r.tag=0;
}
return r; //返回关键字k的位置(或插入位置)
}
2.4.4InsertBTNode函数
功能:关键字k和结点q分别插入到p->key[i+1]和p->ptr[i+1]中
代码实现:
void InsertBTNode(BTNode *&p,int i,KeyType k,BTNode *q){
int j;
for(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!=NULL)
q->parent=p;
p->keynum++;
}
2.4.5SplitBTNode函数
功能:将结点p分裂成两个结点,前一半保留,后一半移入结点q
代码实现:
void SplitBTNode(BTNode *&p,BTNode *&q){
int i;
int s=(m+1)/2;
q=(BTNode *)malloc(sizeof(BTNode)); //给结点q分配空间
q->ptr[0]=p->ptr[s]; //后一半移入结点q
for(i=s+1;i<=m;i++){
q->key[i-s]=p->key[i];
q->ptr[i-s]=p->ptr[i];
}
q->keynum=p->keynum-s;
q->parent=p->parent;
for(i=0;i<=p->keynum-s;i++) //修改双亲指针
if(q->ptr[i]!=NULL)
q->ptr[i]->parent=q;
p->keynum=s-1; //结点p的前一半保留,修改结点p的keynum
}
2.4.6NewRoot函数
功能:生成新的根结点t,原p和q为子树指针
代码实现:
void NewRoot(BTNode *&t,KeyType k,BTNode *p,BTNode *q){