基本操作:
SearchBTree(T,key);
初始条件:
B树T存在,key为和关键字类型相同的给定值。
操作结果:
若T中存在关键字等于key的数据元素,则返回该元素的值或在表中的位置,否则返回“空”。
Insert(T,i,k,P,recptr)
初始条件:
B树q和p存在,i、k是指定变量,recptr指针有效
操作结果:
将k和ap分别插入到q->key[i+1]和q->ptr[i+1],并插入关键字为k的记录recptr
InsertBTree(&T,e);
初始条件:
B树T存在,e为待插入的数据元素。
操作结果:
若T中步存在关键字等于e.key的数据元素,则插入e到T中。
DeleteBTree(&T,key);
初始条件:
B树T存在,key为和关键字类型相同的给定值。
操作结果:
若T中存在其关键字等于key的数据元素,则删除之
BTreeTraverse(BTreeT,Visit)
初始条件:
B树T存在,Visit是对T结点的函数
操作结果:
遍历B树T,对每个结点调用Visit函数
ShowBTree(T);
初始条件:
B树T存在。
操作结果:
以凹入表形式显示B树T。
}ADTBTree
系统时间类型定义:
ADTTime{
数据对象:
D={TM是各种整型类型的系统时间格式定义}
数据关系:
数据元素同属一个集合
基本操作:
GetDate(tm&tim)
初始条件:
系统时间运行
操作结果:
获取系统时间,赋予tm变量tim
AddDate(tm&date2,tmdate1,intday)
初始条件:
系统时间date2、date1、day存在
操作结果:
把date1的日期加day天后赋给date2
Earlier(tmdate1,tmdate2)
初始条件:
系统时间date2、date1存在
操作结果:
比较data1与date2日期的迟与早,如果date1早于或等于date2则返回TRUE,否则返回FALSE。
}ADTTime
图书管理类型定义:
ADTBTree{
数据对象:
D={ai|ai∈BookType,i=1,2,3,……n,n>=0,其中
每个数据元素ai含有类型相同,可惟一标识数据元素的关键字}
数据关系:
数据元素同属一个集合
基本操作:
InitLibrary(&L);
操作结果:
初始化书库L为空书库。
InsertBook(&L,B,result);
初始条件:
书库L和B已存在,result包含B书在书库中的位置或应该插入的位置。
操作结果:
如果书库中已存在B书,则只将B书的库存量增加,否则插入B书到书库L中。
DeleteBook(&L,&B);
初始条件:
书库L和B存在。
操作结果:
如果书库中存在B书,则从书库中删除B书的信息,并返回OK,否则返回ERROR
BorrowBook(L,&B,R);
初始条件:
书库L存在,B书是书库中的书并且可被读者R借阅。
操作结果:
借出一本B书,记录信息。
ReturnBook(L,&B,R);
初始条件:
书库L存在。
操作结果:
若书库L中有读者R借阅B书的记录,则注销该记录,改变B书现存量,并返回OK,书不存在或无该读者记录则返回ERROR。
BespeakBook(L,&B,R);
初始条件:
书库L存在,B书是书库中的书,R为借阅者。
操作结果:
为读者R预约B书。
ListAuthor(L,author);
初始条件:
书库L存在,author为指定作者姓名
操作结果:
显示author的所有著作。
ShowBookinfo(L,B);
初始条件:
书L存在。
操作结果:
若书库L中存在书B,则显示B书基本信息并返回OK,否则返回ERROR。
PrintAllBooks(L);
初始条件:
书库L存在。
操作结果:
显示所有图书基本信息。
}ADTBTree
3.主程序
intmain()
{
系统界面;
初始化;
for(;;)
{
显示菜单信息;
接受命令;
处理命令;
输出结果;
}
}|
3.2、详细设计及各函数说明
/**************************抽象数据类型B-树存储定义*************************/
typedefBookNodeRecord;//记录指针为图书结点类型
typedefstructBTNode{
intkeynum;//结点关键字个数
structBTNode*parent;//指向双亲指针
intkey[m+1];//关键字数组,0号单元未用
structBTNode*ptr[m+1];//指向子树指针
Record*recptr[m+1];//记录指针,0号单元未用
}BTNode,*BTree;//B树节点类型和B树类型
typedefstruct{
BTNode*pt;//指向找到的结点或应该插入的结点
inti;//1...m,在结点中关键字序号
inttag;//1表示查找成功,0表示查找失败
}Result;//B树查找结果类型
/****************************************************************************/
/**************************B-树操作定义************************************/
intSearch(BTreep,intk)
/*在B树p中查找关键字k的位置i,使得p->node[i].key≤K<p->node[i+1].key*/
{
inti;
for(i=0;ikeynum&&p->key[i+1]<=k;i++);
returni;
}
ResultSearchBTree(BTreeT,intk)
//在m阶B树T上查找关键字K,返回结果(pt,i,tag)。
若查找成功,则特征值
//tag=1,指针pt所指结点中第i个关键字等于K;否则特征值tag=0,等于K的
//关键字应插入在指针Pt所指结点中第i和第i+1个关键字之间。
{
Resultr;
inti=1;
BTreep=T,q=NULL;//初始化,p指向待查结点,q指向p的双亲
intfound=FALSE;
while(p&&!
found){
i=Search(p,k);//在p->key[1...keynum]中查找
if(i&&p->key[i]==k)found=TRUE;//找到待查关键字
else
{
q=p;
p=p->ptr[i];
}
}
if(found)
{
r.pt=p;
r.i=i;
r.tag=1;
}
else
{
r.pt=q;
r.i=i;
r.tag=0;
}
returnr;
}
voidInsert(BTree&q,inti,intk,BTreeap,Record*recptr)
//将k和ap分别插入到q->key[i+1]和q->ptr[i+1],并插入关键字为k的记录recprt
{
for(intj=q->keynum;j>i;--j)
{//记录、关键字、子树指针后移
q->key[j+1]=q->key[j];
q->ptr[j+1]=q->ptr[j];
q->recptr[j+1]=q->recptr[j];
}
q->key[i+1]=k;//插入记录、关键字、子树指针,关键字个数加1
q->ptr[i+1]=ap;
q->recptr[i+1]=recptr;
q->keynum++;
if(ap)ap->parent=q;//子树ap的父亲指针
}
voidSplit(BTree&q,intn,BTree&ap)
//以n为分界将结点q分裂为两个结点,前一半保留,后一半移入新生结点ap
{
inti;
ap=(BTree)malloc(sizeof(BTNode));//申请新结点ap
ap->ptr[0]=q->ptr[n];
for(i=n+1;i<=m;i++)//n后的关键字、子树指针、记录转移到ap
{
ap->key[i-n]=q->key[i];
ap->ptr[i-n]=q->ptr[i];
ap->recptr[i-n]=q->recptr[i];
}
ap->keynum=q->keynum-n;//计算ap的关键字个数
q->keynum=n-1;//q的关键字个数减少
ap->parent=q->parent;
for(i=0;i<=m-n;i++)
if(ap->ptr[i])ap->ptr[i]->parent=ap;//ap的子树的父亲指针
}
voidNewRoot(BTree&T,BTreep,intk,BTreeap,Record*recptr)
//当插入B树时T为空或根结点分裂为p和ap两个节点,需建立一个根节点空间
//本函数为T申请一块空间,插入p,k,ap和记录rec
{
T=(BTree)malloc(sizeof(BTNode));
T->keynum=1;
T->ptr[0]=p;//插入
T->ptr[1]=ap;
T->key[1]=k;
T->recptr[1]=recptr;
if(p)p->parent=T;//T的子树ap的父亲指针
if(ap)ap->parent=T;
T->parent=NULL;//根节点双亲为NULL
}
intInsertBTree(BTree&T,intk,BTreeq,inti,Record*recptr)
//在m阶B树T上结点*q的key[i]与key[i+1]之间插入关键字K和记录rec。
//若引起结点过大,则沿双亲链进行必要的结点分裂调整,使T仍是m阶B树。
{
BTreeap=NULL;
intfinished=FALSE;//T是空树,生成仅含关键字K的根结点*T
if(!
q)NewRoot(T,NULL,k,NULL,recptr);
else{
while(!
finished)
{
Insert(q,i,k,ap,recptr);/将k和ap插入到q->key[i+1]和q->ptr[i+1]
if(q->keynumelse{
Split(q,(m+1)/2,ap);//分裂结点q
k=q->key[(m+1)/2];
recptr=q->recptr[(m+1)/2];
if(q->parent)
{//在双亲结点*q中查找k的插入位置
q=q->parent;
i=Search(q,k);
}
elsefinished=OVERFLOW;//根节点已分裂为*q和*ap两个结点
}
}
if(finished==OVERFLOW)//根结点已分裂为结点*q和*ap
NewRoot(T,q,k,ap,recptr);//需生成新根结点*T,q和ap为子树指针
}
returnOK;
}
voidTakePlace(BTree&q,int&i)
//*q结点的第i个关键字为k,用q的后继关键字替代q,且令q指向后继所在结点
{
BTreep=q;
q=q->ptr[i];
while(q->ptr[0])q=q->ptr[0];//查找p的后继
p->key[i]=q->key[1];//关键字代替
p->recptr[i]=q->recptr[1];//记录代替
i=1;//代替后应该删除q所指结点的第1个关键字
}
voidDel(BTreeq,inti)
//删除q所指结点第i个关键字及其记录
{
for(;ikeynum;i++)//关键字和记录指针前移
{
q->key[i]=q->key[i+1];
q->recptr[i]=q->recptr[i+1];
}
q->keynum--;//关键字数目减1
}
intBorrow(BTreeq)
//若q的兄弟结点关键字大于(m-1)/2,则从兄弟结点上移最小(或最大)的关键字到双亲结点,
//而将双亲结点中小于(或大于)且紧靠该关键字的关键字下移至q中,并返回OK,否则返回EREOR。
{
inti;
BTreep=q->parent,b;//p指向q的双亲结点
for(i=0;p->ptr[i]!
=q;i++);//查找q在双亲p的子树位置
if(i>=0&&i+1<=p->keynum&&p->ptr[i+1]->keynum>(m-1)/2)
{//若q的右兄弟关键字个数大于(m-1)/2
b=p->ptr[i+1];//b指向右兄弟结点
q->ptr[1]=b->ptr[0];//子树指针也要同步移动
q->key[1]=p->key[i+1];//从父节点借第i+1个关键字
q->recptr[1]=p->recptr[i+1];
p->key[i+1]=b->key[1];//b第一个关键字上移到父节点
p->recptr[i+1]=b->recptr[1];
for(i=1;i<=b->keynum;i++)//b第一个关键字上移,需把剩余记录前移一位
{
b->key[i]=b->key[i+1];
b->recptr[i]=b->recptr[i+1];
b->ptr[i-1]=b->ptr[i];
}
}
elseif(i>0&&p->ptr[i-1]->keynum>(m-1)/2)
{//若q的左兄弟关键字个数大约(m-1)/2
b=p->ptr[i-1];//b指向左兄弟结点
q->ptr[1]=q->ptr[0];
q->ptr[0]=b->ptr[b->keynum];
q->key[1]=p->key[i];//从父节点借第i个关键字
q->recptr[1]=p->recptr[i];
p->key[i]=b->key[b->keynum];//将b最后一个关键字上移到父节点
p->recptr[i]=b->recptr[b->keynum];
}
elsereturnERROR;//无关键字大于(m-1)/2的兄弟
q->keynum++;
b->keynum--;
for(i=0;i<=q->keynum;i++)
if(q->ptr[i])q->ptr[i]->parent=q;//刷新q的子结点的双亲指针
returnOK;
}
voidCombine(BTree&q)
//将q剩余部分和q的父结点的相关关键字合并到q兄弟中,然后释放q,令q指向修改的兄弟
{
inti,j;
BTreep=q->parent,b;//p指向q的父亲
for(i=0;p->ptr[i]!
=q;i++);//插好q在父亲p中的子树位置
if(i==0)//如为0,则需合并为兄弟的第一个关键字
{
b=p->ptr[i+1];
for(j=b->keynum;j>=0;j--)//将b的关键字和记录后移一位
{
b->key[j+1]=b->key[j];
b->recptr[j+1]=b->recptr[j];
b->ptr[j+1]=b->ptr[j];
}
b->ptr[0]=q->ptr[0];//合并
b->key[1]=p->key[1];
b->recptr[1]=p->recptr[1];
}
elseif(i>0)//若q在父亲的子树位置大约0
{//需合并为兄弟b的最后一个关键字
b=p->ptr[i-1];
b->key[b->keynum+1]=p->key[i];//合并
b->recptr[b->keynum+1]=p->recptr[i];
b->ptr[b->keynum+1]=q->ptr[0];
}
if(i==0||i==1)//若i为0或1,需将父节点p关键字前移一位
for(;ikeynum;i++)
{
p->key[i]=p->key[i+1];
p->ptr[i]=p->ptr[i+1];
p->recptr[i]=p->recptr[i+1];
}
p->keynum--;
b->keynum++;
free(q);
q=b;//q指向修改的兄弟结点
for(i=0;i<=b->keynum;i++)
if(b->ptr[i])b->ptr[i]->parent=b;//刷新b的子结点的双亲指针
}
intDeleteBTree(BTree&T,intk)
//在m阶B树T上删除关键字k及其对应记录,并返回OK。
//如T上不存在关键字k,则返回ERROR。
{
intx=k;
BTreeq,b=NULL;
intfinished=FALSE,i=1;
Resultres=SearchBTree(T,k);//在T中查找关键字k
if(res.tag==0)returnER