1. 程式人生 > >C語言-B樹(B-樹)的完整實現

C語言-B樹(B-樹)的完整實現

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){
    t=(BTNode *)malloc(sizeof(BTNode));             //分配空間 
    t->keynum=1;
    t->ptr[0]=p;
    t->ptr[1]=q;
    t->key[1]=k;
    if(p!=NULL)                                     //調整結點p和q的雙親指標 
        p->parent=t;
    if(q!=NULL) 
        q->parent=t;
    t->parent=NULL;
}
2.4.7InsertBTree函式
功能:在樹t中插入關鍵字k,返回插入結果
程式碼實現:
void InsertBTree(BTree &t,int i,KeyType k,BTNode *p){
    BTNode *q;
    int finish_tag,newroot_tag,s;                   //設定需要新結點標誌和插入完成標誌 
    KeyType x;
    if(p==NULL)                                     //t是空樹
        NewRoot(t,k,NULL,NULL);                     //生成僅含關鍵字k的根結點t
    else{
        x=k;
        q=NULL;
        finish_tag=0;       
        newroot_tag=0;
        while(finish_tag==0&&newroot_tag==0){
            InsertBTNode(p,i,x,q);                  //將關鍵字x和結點q分別插入到p->key[i+1]和p->ptr[i+1]
            if (p->keynum<=Max) 
                finish_tag=1;                       //插入完成
            else{         
                s=(m+1)/2;
                SplitBTNode(p,q);                 //分裂結點 
                x=p->key[s];
                if(p->parent){                      //查詢x的插入位置
                    p=p->parent;
                    i=SearchBTNode(p, x);
                }
                else                            //沒找到x,需要新結點 
                    newroot_tag=1;
            }
        }
        if(newroot_tag==1)                      //根結點已分裂為結點p和q 
            NewRoot(t,x,p,q);                   //生成新根結點t,p和q為子樹指標
    }
}
2.4.8Remove函式
功能:從p結點刪除key[i]和它的孩子指標ptr[i]
程式碼實現:
void Remove(BTNode *p,int i){
    int j;
    for(j=i+1;j<=p->keynum;j++){                    //前移刪除key[i]和ptr[i]
        p->key[j-1]=p->key[j];
        p->ptr[j-1]=p->ptr[j];
    }
    p->keynum--;
}
2.4.9Substitution函式
功能:尋找替代值(右子樹中最小的關鍵字)
程式碼實現:
void Substitution(BTNode *p,int i){
    BTNode *q;
    for(q=p->ptr[i];q->ptr[0]!=NULL;q=q->ptr[0]);
    p->key[i]=q->key[1];                            //複製關鍵字值
}
2.4.10MoveRight函式
功能:雙親結點p中的最後一個關鍵字移入右結點q中
將左結點aq中的最後一個關鍵字移入雙親結點p中
程式碼實現:
void MoveRight(BTNode *p,int i){    
int j;
    BTNode *q=p->ptr[i];
    BTNode *aq=p->ptr[i-1];

    for(j=q->keynum;j>0;j--){                       //將右兄弟q中所有關鍵字向後移動一位
        q->key[j+1]=q->key[j];
        q->ptr[j+1]=q->ptr[j];
    }

    q->ptr[1]=q->ptr[0];                            //從雙親結點p移動關鍵字到右兄弟q中
    q->key[1]=p->key[i];
    q->keynum++;

    p->key[i]=aq->key[aq->keynum];                  //將左兄弟aq中最後一個關鍵字移動到雙親結點p中
    p->ptr[i]->ptr[0]=aq->ptr[aq->keynum];
    aq->keynum--;
}
2.4.11MoveLeft函式
功能:將雙親結點p中的第一個關鍵字移入左結點aq中,
將右結點q中的第一個關鍵字移入雙親結點p中
程式碼實現:
void MoveLeft(BTNode *p,int i){ 
    int j;
    BTNode *aq=p->ptr[i-1];
    BTNode *q=p->ptr[i];

    aq->keynum++;                                   //把雙親結點p中的關鍵字移動到左兄弟aq中
    aq->key[aq->keynum]=p->key[i]; 
    aq->ptr[aq->keynum]=p->ptr[i]->ptr[0];

    p->key[i]=q->key[1];                            //把右兄弟q中的關鍵字移動到雙親節點p中
    q->ptr[0]=q->ptr[1];
    q->keynum--;

    for(j=1;j<=aq->keynum;j++){                     //將右兄弟q中所有關鍵字向前移動一位
        aq->key[j]=aq->key[j+1];
        aq->ptr[j]=aq->ptr[j+1];
    }
}
2.4.12Combine函式
功能:雙親結點p、右結點q合併入左結點aq,
並調整雙親結點p中的剩餘關鍵字的位置
程式碼實現:
void Combine(BTNode *p,int i){
    int j;
    BTNode *q=p->ptr[i];                            
    BTNode *aq=p->ptr[i-1];

    aq->keynum++;                                  //將雙親結點的關鍵字p->key[i]插入到左結點aq     
    aq->key[aq->keynum]=p->key[i];
    aq->ptr[aq->keynum]=q->ptr[0];

    for(j=1;j<=q->keynum;j++){                      //將右結點q中的所有關鍵字插入到左結點aq 
        aq->keynum++;
        aq->key[aq->keynum]=q->key[j];
        aq->ptr[aq->keynum]=q->ptr[j];
    }

    for(j=i;j<p->keynum;j++){                       //將雙親結點p中的p->key[i]後的所有關鍵字向前移動一位 
        p->key[j]=p->key[j+1];
        p->ptr[j]=p->ptr[j+1];
    }
    p->keynum--;                                    //修改雙親結點p的keynum值 
    free(q);                                        //釋放空右結點q的空間
}
2.4.13AdjustBTree函式
功能:刪除結點p中的第i個關鍵字後,調整B樹
程式碼實現:
void AdjustBTree(BTNode *p,int i){
    if(i==0)                                        //刪除的是最左邊關鍵字
        if(p->ptr[1]->keynum>Min)                   //右結點可以借
            MoveLeft(p,1);
        else                                        //右兄弟不夠借 
            Combine(p,1);
    else if(i==p->keynum)                           //刪除的是最右邊關鍵字
        if(p->ptr[i-1]->keynum>Min)                 //左結點可以借 
            MoveRight(p,i);
        else                                        //左結點不夠借 
            Combine(p,i);
    else if(p->ptr[i-1]->keynum>Min)                //刪除關鍵字在中部且左結點夠借 
        MoveRight(p,i);
    else if(p->ptr[i+1]->keynum>Min)                //刪除關鍵字在中部且右結點夠借 
        MoveLeft(p,i+1);
    else                                            //刪除關鍵字在中部且左右結點都不夠借
        Combine(p,i);
}
2.4.14BTNodeDelete函式
功能:在結點p中查詢並刪除關鍵字k
程式碼實現:
int BTNodeDelete(BTNode *p,KeyType k){
    int i;
    int found_tag;                                  //查詢標誌 
    if(p==NULL)                                     
        return 0;
    else{
        found_tag=FindBTNode(p,k,i);                //返回查詢結果 
        if(found_tag==1){                           //查詢成功 
            if(p->ptr[i-1]!=NULL){                  //刪除的是非葉子結點
                Substitution(p,i);                  //尋找相鄰關鍵字(右子樹中最小的關鍵字) 
                BTNodeDelete(p->ptr[i],p->key[i]);  //執行刪除操作 
            }
            else
                Remove(p,i);                        //從結點p中位置i處刪除關鍵字
        }
        else
            found_tag=BTNodeDelete(p->ptr[i],k);    //沿孩子結點遞迴查詢並刪除關鍵字k
        if(p->ptr[i]!=NULL)
            if(p->ptr[i]->keynum<Min)               //刪除後關鍵字個數小於MIN
                AdjustBTree(p,i);                   //調整B樹 
        return found_tag;
    }
}
2.4.15BTreeDelete函式
功能:構建刪除框架,執行刪除操作
程式碼實現:
void BTreeDelete(BTree &t,KeyType k){  
    BTNode *p;
    int a=BTNodeDelete(t,k);                        //刪除關鍵字k 
    if(a==0)                                        //查詢失敗 
        printf("   關鍵字%d不在B樹中\n",k);
    else if(t->keynum==0){                          //調整 
        p=t;
        t=t->ptr[0];
        free(p);
    }
}
2.4.16DestroyBTree函式
功能:遞迴釋放B樹
程式碼實現:
void DestroyBTree(BTree &t){
    int i;  
    BTNode* p=t;  
    if(p!=NULL){                                    //B樹不為空  
        for(i=0;i<=p->keynum;i++){                  //遞迴釋放每一個結點 
            DestroyBTree(*&p->ptr[i]);  
        }  
        free(p);  
    }  
    t=NULL;  
}  
2.4.17InitQueue函式
功能:初始化佇列
程式碼實現: 
Status InitQueue(LinkList &L){
    L=(LNode*)malloc(sizeof(LNode));                //分配結點空間 
    if(L==NULL)                                     //分配失敗              
        return OVERFLOW;
     L->next=NULL;
     return OK;
}
2.4.18CreateNode函式
功能:新建一個結點
程式碼實現:
LNode* CreateNode(BTNode *p){
    LNode *q;
    q=(LNode*)malloc(sizeof(LNode));                //分配結點空間
    if(q!=NULL){                                    //分配成功 
        q->data=p;
        q->next=NULL;
    }
   return q;
}
2.4.19Enqueue函式
功能:元素q入佇列
程式碼實現:
Status Enqueue(LNode *p,BTNode *q){ 
    if(p==NULL)                                     
        return ERROR;                               
    while(p->next!=NULL)                            //調至佇列最後 
        p=p->next;
    p->next=CreateNode(q);                          //生成結點讓q進入佇列 
    return OK;
}
2.4.20Dequeue函式
功能:出佇列,並以q返回值
程式碼實現: 
Status Dequeue(LNode *p,BTNode *&q){
    LNode *aq;
    if(p==NULL||p->next==NULL)                      //刪除位置不合理 
        return ERROR; 
    aq=p->next;                                     //修改被刪結點aq的指標域
    p->next=aq->next;                               
    q=aq->data;
    free(aq);                                       //釋放結點aq
    return OK;
}
2.4.21IfEmpty函式
功能:佇列判空 
程式碼實現:
Status IfEmpty(LinkList L){
    if(L==NULL)                                     //佇列不存在 
        return ERROR;
    if(L->next==NULL)                               //佇列為空 
        return TRUE;
    return FALSE;                                   //佇列非空 
}
2.4.22DestroyQueue函式
功能:銷燬佇列
程式碼實現:
void DestroyQueue(LinkList L){
   LinkList p;
    if(L!=NULL){
        p=L;
        L=L->next;
        free(p);                                    //逐一釋放 
        DestroyQueue(L);
    }
}
2.4.23Traverse函式
功能:用佇列遍歷輸出B樹
程式碼實現:
Status Traverse(BTree t,LinkList L,int newline,int sum){ 
    int i;
    BTree p;
    if(t!=NULL){
        printf("  [ ");
        Enqueue(L,t->ptr[0]);                       //入隊         
        for(i=1;i<=t->keynum;i++){
            printf(" %d ",t->key[i]);
            Enqueue(L,t->ptr[i]);                   //子結點入隊 
        }
        sum+=t->keynum+1;
        printf("]");
        if(newline==0){                             //需要另起一行 
            printf("\n");
            newline=sum-1;
            sum=0;
        }
        else
            newline--;
     }

     if(IfEmpty(L)==FALSE){                         //l不為空 
         Dequeue(L,p);                              //出隊,以p返回 
         Traverse(p,L,newline,sum);                 //遍歷出隊結點 
     }
     return OK;
 }
2.4.24PrintBTree函式
功能:輸出B樹
程式碼實現:
Status PrintBTree(BTree t){
   LinkList L;
    if(t==NULL){
        printf("  B樹為空樹");
        return OK;
    }
    InitQueue(L);                                   //初始化佇列 
    Traverse(t,L,0,0);                              //利用佇列輸出 
    DestroyQueue(L);                                //銷燬佇列 
    return OK;
}
2.4.25Test1函式
功能:測試B樹功能
程式碼實現:
void Test1(){ 
    system("color 70");  
    BTNode *t=NULL;
    Result s;                                       //設定查詢結果 
    int j,n=15;
    KeyType k;
    KeyType a[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};                           
    printf("建立一棵%d階B樹:\n",m);
    for(j=0;j<n;j++){                               //逐一插入元素 
        s=SearchBTree(t,a[j]);
        if(s.tag==0)
            InsertBTree(t,s.i,a[j],s.pt);
        printf("   第%d步,插入元素%d:\n ",j+1,a[j]);
        PrintBTree(t);
    }

    printf("\n");
    printf("刪除操作:\n");                          //刪除操作 
    k=9;                                                        
    BTreeDelete(t,k);
    printf("  刪除%d:\n ",k);
    printf("  刪除後的B樹: \n");
    PrintBTree(t);
    printf("\n");

    k=1;
    BTreeDelete(t,k);
    printf("  刪除%d:\n ",k);
    printf("  刪除後的B樹: \n");
    PrintBTree(t);
    printf("\n");

    printf("  遞迴釋放B樹\n");                       //遞迴釋放B樹
    DestroyBTree(t);                                 
    PrintBTree(t);
} 
2.4.26Test2函式
功能:測試B樹功能
程式碼實現:
void Test2(){
    int i,k; 
    system("color 70");
    BTree t=NULL;  
    Result s;                                       //設定查詢結果 
    while(1){
    printf("此時的B樹:\n");
    PrintBTree(t); 
    printf("\n");
    printf("=============Operation Table=============\n");
    printf("   1.Init     2.Insert    3.Delete    \n");
    printf("   4.Destroy  5.Exit      \n");
    printf("=========================================\n");
    printf("Enter number to choose operation:_____\b\b\b");
    scanf("%d",&i);
    switch(i){
        case 1:{
            InitBTree(t);
            printf("InitBTree successfully.\n");
            break;
        }


        case 2:{
            printf("Enter number to InsertBTree:_____\b\b\b");
            scanf("%d",&k);
            s=SearchBTree(t,k);
            InsertBTree(t,s.i,k,s.pt);
            printf("InsertBTree successfully.\n");
            break;
        }
        case 3:{
            printf("Enter number to DeleteBTree:_____\b\b\b");
            scanf("%d",&k);
            BTreeDelete(t,k);
            printf("\n");
            printf("DeleteBTree successfully.\n");
            break;
        }
        case 4:{
            DestroyBTree(t);
            break;
            printf("DestroyBTree successfully.\n");
        }
        case 5:{
            exit(-1);               
            break;
        }
    }
 }
}

三.功能測試
插入功能測試/遍歷功能測試
依次插入1-15進行測試輸出,結果如下:
這裡寫圖片描述
由輸出的B樹可知,插入功能正常並且遍歷功能正常
3.2刪除功能測試
在之前插入1-15後進行刪除關鍵字的功能測試,選取9和1依次進行刪除測試,結果如下:
這裡寫圖片描述
根據B樹的定義和該B樹的輸出,刪除功能正常
3.3釋放功能測試
在之前的基礎上進行遞迴釋放B樹功能測試,結果如下:
這裡寫圖片描述
遍歷輸出的結果為B數為空樹,說明釋放功能正常
3.4其他功能測試
其他介面在以上功能中已經有所體現,均正常,不再一一呼叫測試。
四.思考與小結
錯誤總結
(1)在部分需要判空的地方沒有判空
(2)遞迴實現的時候多次爆棧
(3)插入分裂的SplitBTNode函式一開始寫的時候分裂成兩個
(4)刪除操作中的Combine函式的指標忘記調整
4.2部分優化
4.2.1輸出優化
在一開始輸出的時候選用的是括號輸出法(測試功能選用的值略有不同),在直觀上比較難的去分辨哪些是雙親結點的左右結點,因此在輸出函式上進行了優化
這裡寫圖片描述
通過佇列遍歷,在每一次遍歷的過程中能夠,模擬層次遍歷,在B樹的結構上更加美觀,而且更容易看清楚B樹的結構
這裡寫圖片描述
4.2.2測試介面優化
在保持原本介面不變的情況下,寫了Test2函式,自行建立和進行各種B樹的操作.
這裡寫圖片描述

整個程式原始碼
標頭檔案

#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為關鍵字型別

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 

BTree程式碼

#include"BTREE.h"
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h> 

Status InitBTree(BTree &t){
//初始化B樹 
    t=NULL;
    return OK;
}


int SearchBTNode(BTNode *p,KeyType k){
//在結點p中查詢關鍵字k的插入位置i 
    int i=0;
    for(i=0;i<p->keynum&&p->key[i+1]<=k;i++);
    return i;
}


Result SearchBTree(BTree t,KeyType k){
/*在樹t上查詢關鍵字k,返回結果(pt,i,tag)。若查詢成功,則特徵值
tag=1,關鍵字k是指標pt所指結點中第i個關鍵字;否則特徵值tag=0,
關鍵字k的插入位置為pt結點的第i個*/

    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,使得p->key[i]<=k<p->key[i+1]
        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的位置(或插入位置)
}


void InsertBTNode(BTNode *&p,int i,KeyType k,BTNode *q){
//將關鍵字k和結點q分別插入到p->key[i+1]和p->ptr[i+1]中
    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++;
}


void SplitBTNode(BTNode *&p,BTNode *&q){
//將結點p分裂成兩個結點,前一半保留,後一半移入結點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;