B树的基本操作(统计关键码和打印)含测试原码

删除算法

设计一个算法在一棵B树上删除关键码k。若B树中没有等于k的关键码,则不删除。
【解答】:想要在B树上删除一个关键码,首先需要找到这个关键码所在的结点,若该节点不是叶节点,且被删关键码为 K i K_i Ki,1<=i<=n,则在删除关键码之后,应以该节点 p I p_I pI所指向子树中的最小关键码x来代替被删关键码 K i K_i Ki所在的位置,然后在x所在的叶结点中删除x,在叶结点上的删除有4种情况:

  1. 若该叶结点1又是根结点时,在删除前该节点中关键码个数n>=2,则直接删去关键码即可,若n<2则删除后b树变空,删除结束。
    若该节点不是根节点,则
  2. 若删除前该节点关键码个数n>=[m/2],则直接删去该关键码,删除结束。
  3. 若删除前该节点关键码个数n=[m/2]-1,若此时右兄弟(左兄弟) 结点的关键码个数n>=[m/2],则可进行过关键码移动,以达到新的平衡.
  4. 若删除前结点关键码个数n = [m/2]-1,此时右兄弟(或左兄弟)结点的关键码个数n=[m/2]-1,则可进行结点合并,以达到新的平衡
void merge(BTNode *p,BTNode *pr,BTNode *q,int i ){
    
    
    /* *p 是*pr的第i个子女,算法让*p让其右兄弟*q合并,保留*p结点
     * 双亲*pr 的key[i+1]下落到*p
     * */
    p->key[(p->n)+1] = pr->key[i+1]; //从双亲*pr 下降关键码key[i+1]
    p->ptr[(p->n)+1] = q->ptr[0]; //从右兄弟*q左移指针q->str[0]
    if(q->ptr[0] != NULL) q->ptr[0]->parent = p;
    for(int k = 1;k<=q->n;k++) {
    
     //右结点其他信息左移
        p->key[(p->n)+k+1] = q->key[k];
        p->ptr[(p->n)+k+1] = q->ptr[k];
        if(q->ptr[k] !=NULL )q->ptr[k] ->parent = p;
    }
    p->n = (p->n)+(q->n) + 1; //修改*p 中关键码个数
    free(q);                  //释放*q
    for(int k = i+2;k<=pr->n;k++) //双亲结点*pr压缩
    {
    
    
        pr->key[k-1] = pr->key[k];
        pr->ptr[k-1] = pr->ptr[k];
    }
    pr->n--;
}
bool LeftAdjust(BTNode *p,BTNode *pr,int d,int j){
    
    
    //*p是其双亲*pr 的第j个子女,*p与*pr,右兄弟*q一起调整
    BTNode  *q = pr->ptr[j+1];
    int k;    //*p的右兄弟
    if(q->n > d-1){
    
    //右兄弟空间够,做移动
        p->key[p->n+1] = pr->key[j+1];//双亲结点相应关键码下移
        pr->key[j+1] = q->key[1]; //右兄弟关键码上移
        p->ptr[p->n+1] = q->ptr[0]; //右兄弟最左指针左移
        if(q->ptr[0] != NULL) q->ptr[0]->parent = p;
        for(k=1;k<=q->n;k++) q->ptr[k-1] = q->ptr[k];
        for(k=2;k<=q->n;k++) q->key[k-1] = q->key[k];
        p->n++;
        q->n--;
        return false;
    }else{
    
    
     merge(p,pr,q,j);
     return true; //p与q合并
    }
}
bool RightAdjust(BTNode *p,BTNode *pr,int d,int j){
    
    
    //*p是其双亲*pr 的第j个子女,*p与*pr,左兄弟*q一起调整
    BTNode  *q = pr->ptr[j-1];
    int k;    //*p的左兄弟
    if(q->n > d-1){
    
    //左兄弟空间够,做移动
        for(k=p->n;k>=0;k--) p->ptr[k+1] = p->ptr[k];
        for(k=p->n;k>=1;k--) p->key[k+1] = p->key[k];
        p->ptr[0] = q->ptr[q->n];//左兄弟最后指针右移
        if(q->ptr[q->n] != NULL) q->ptr[q->n]->parent = p;
        p->key[1] = pr->key[j]; //双亲相应关键码下移
        pr->key[j] = q->key[q->n];//左兄弟最后关键码上衣移
        q->n--;
        p->n++;
        return false;
    }else{
    
    
        merge(q,pr,p,j-1);
        return true; //p与q合并
    }
}

bool Remove(BTree &T,KeyType k){
    
    
    //算法调用方式bool succ = remove(T,k)
    //输入B树的根指针t,删除关键码k
    //成功true,错误false
    int i,j,d = (m+1)/2;
    BTNode *p,*pr,*s;
    bool succ;
    if(!Search(T,k,p,i)) return false; //查找k失败,返回
    if(p->ptr[i]!= NULL){
    
      //若p是非叶节点
        s = p->ptr[i];
        pr = p;  //查找K[i]的右子树左下节点pr
        while(s!=NULL) {
    
    
            pr = s;
            s = s->ptr[0];
        }
        p->key[i] = pr->key[1];//用此结点最小关键码寻找
        for(j=2;j<=pr->n;j++){
    
    
            pr->key[j-1] = pr->key[j];
            pr->ptr[j-1] = pr->ptr[j];
        }
        pr->n--; //叶结点关键码个数减1
        p = pr; //下一步处理q结点中的删除
    }else{
    
    
        for(j = i+1;j<=p->n;j++){
    
    
            p->key[j-1] = p->key[j];
            p->ptr[j-1] = p->ptr[j];
        }
        p->n--; //叶节点关键码个数减1
    }
    while(1){
    
    
        if(p->n < d-1){
    
    
            pr = p->parent;
            for(j=0;j<=pr->n && pr->ptr[j] != p;j++);
            if(j==0)
                succ = LeftAdjust(p,pr,d,j);
            else
                succ = RightAdjust(p,pr,d,j);
            if(succ){
    
      //继续向上做节点调整工作
                p = pr;
                if(p==T) break;
            }
        }else break;    //不小于d-1,无需调整,跳出
    }
    if(T->n == 0){
    
       //当根节点为空删除节点
        p = T->ptr[0];
        free(T);
        T = p;
        T->parent = NULL;
    }
    return true;
}

统计B树关键码个数

采用深度优先就行了

void count_Keynum(BTree T,int& count){
    
    
    if(T != NULL){
    
    
        count = count+ T->n;
        for(int i =0;i<=T->n;i++)  //累加关键码个数
            count_Keynum(T[i],count);//进入子树累加
    }
}

遍历B树,从小到大打印树值

也是深度优先遍历,有一种先序遍历的感觉

void Traversal(BTree T){
    
    
    if(T!=NULL){
    
    
        Traversal(T->ptr[0]);
        for(int i =1;i<=T->n;i++){
    
    
            printf("%d ",T->key[i]);
            Traversal(T->ptr[i]);
        }
    }

}

在这里插入图片描述

创建B树

用B树的插入算法Insert(T,k),从空树开始,输入一连串关键码a0,a1,…,创建一颗B树,约定输入结束标志是finish,这是一个特定的关键码。

void createBTree(BTree &T){
    
    
    //createBTree(T),输入:B树根指针T;输出;逐个输入数据
    KeyType a,finish = 0;
    T = NULL;
    scanf("%d",&a);
    while(a!=finish){
    
    
        Insert(T,a); //将输入的关键码插入到B树。
        scanf("%d",&a);
    }

}

完整源码

//完成P109算法设计题第(3)题的算法设计与实现,并编写程序进行验证
#include<iostream>
#define MaxValue 255
#define m 5            //b树的阶数

using namespace std;

typedef int KeyType;

typedef struct node{
    
    
    int n;                //结点内关键词个数
    struct node*parent;  //双亲结点指针
    int pno;             //双亲结点指针
    KeyType  key[m+1];  //key[m]为监视韶兼工作单元 key[0]未用
    struct node*ptr[m+1]; //子树结点指针数组,ptr[m]在插入溢出时用
    int *recptr[m+1];   //每个索引项指向数据区记录地址的指针
}BTNode,*BTree;        //B树的定义

//对b树进行查找k
//b树的阶为m,最大高度h<= log[m/2]((n+1)/2)+1,最小高度为h>=logm(n+1) 查找树的比较次数不超过m*h
bool Search(BTree T,KeyType k,BTNode *&p,int &i){
    
    
    BTNode *pre;  //p来扫描,pre是双亲指针
    p = T;
    pre = NULL;
    while(p!=NULL){
    
                  //从根开始检测
        i = 0;
        p->key[(p->n)+1] = MaxValue;
        while(p->key[i+1] <k) i++;
        if(p->key[i+1] == k) {
    
      //在结点内顺序查找
            i++;
            return true; //查找成功,本节点有k
        }
        pre = p;
        p = p->ptr[i];   //本节点无k下降到子树
    }
    p = pre;
    i++;
    return false;
}
//对b树进行查找
//m阶b树的高度为k,插入的算法比较次数不超过m*h,读写次数3h+1
bool Insert(BTree &T,KeyType k){
    
    
    int i,j,s = (m+1)/2;
    BTNode *p,*ap,*q;
    if(T==NULL){
    
    
        T = (BTNode *)malloc(sizeof(BTNode));
        T->ptr[0] = T->ptr[1] = NULL;
        T->key[1] = k;
        T->parent = NULL;
        T->n = 1;
        return true;
    }
    if(Search(T,k,p,i)) return false;
    ap = NULL;
    while(1){
    
    
        if(i> p->n) {
    
    
            p->key[i] = k;
            p->ptr[i] = ap;
        }else{
    
    
            for(j = p->n;j>=i;j--){
    
         //空出结点p第i个位置
                p->key[j+1] = p->key[j];
                p->ptr[j+1] = p->ptr[j];
            }
            p->key[j+1] = k;  //插入关键词k
            p->ptr[j+1] = ap;
        }
        (p->n)++;              //结点关键词个数+1
        if(p->n == m){
    
          //结点关键吗个数超出上限
            q = (BTNode *)malloc(sizeof(BTNode)); //分裂,创建新结点q
            q->ptr[0] = p->ptr[s];                //传送p的后半部分q
            for(j = s+1;j<=m;j++){
    
    
                q->key[j-s] = p->key[j];
                q->ptr[j-s] = p->ptr[j];
            }
            p->n = s-1;
            q->n = m-s;
            q->parent = p->parent;
            for(j=0;j<=q->n;j++)
                if(q->ptr[j]!=NULL) q->ptr[j]->parent = q;
            k= p->key[s]; //(k,ap)形成向上插入二元组
            ap = q;
            if(p->parent != NULL){
    
                //分裂的不是根节点
                p = p->parent;               //转向双亲插入中间关键吗
                for(i=1;i<=p->n&&p->key[i]<k;i++);
            }else{
    
    
                T = (BTNode *) malloc(sizeof(BTNode));
                T->ptr[0] =p;
                T->ptr[1] = ap;
                T->key[1] = k;
                p->parent = T;
                ap->parent =  T;
                T->parent = NULL;
                T->n = 1;
                return true;       //新根结点创建完,返回
            }

        }
        else return true;   //插入后结点不溢出,返回
    }
}
//遍历

void preTraversal(BTree T,int k){
    
    
    if(T !=NULL){
    
    
        printf("level = %d,n=%d ",k,T->n);
        for(int i =1;i<=T->n;i++)
            printf(" %d ",T->key[i]);
        printf("\n");
        for(int i=0;i<=T->n;i++)
            preTraversal(T->ptr[i],k+1);   //递归遍历1第i课子树
    }
}

void merge(BTNode *p,BTNode *pr,BTNode *q,int i ){
    
    
    /* *p 是*pr的第i个子女,算法让*p让其右兄弟*q合并,保留*p结点
     * 双亲*pr 的key[i+1]下落到*p
     * */
    p->key[(p->n)+1] = pr->key[i+1]; //从双亲*pr 下降关键码key[i+1]
    p->ptr[(p->n)+1] = q->ptr[0]; //从右兄弟*q左移指针q->str[0]
    if(q->ptr[0] != NULL) q->ptr[0]->parent = p;
    for(int k = 1;k<=q->n;k++) {
    
     //右结点其他信息左移
        p->key[(p->n)+k+1] = q->key[k];
        p->ptr[(p->n)+k+1] = q->ptr[k];
        if(q->ptr[k] !=NULL )q->ptr[k] ->parent = p;
    }
    p->n = (p->n)+(q->n) + 1; //修改*p 中关键码个数
    free(q);                  //释放*q
    for(int k = i+2;k<=pr->n;k++) //双亲结点*pr压缩
    {
    
    
        pr->key[k-1] = pr->key[k];
        pr->ptr[k-1] = pr->ptr[k];
    }
    pr->n--;
}
bool LeftAdjust(BTNode *p,BTNode *pr,int d,int j){
    
    
    //*p是其双亲*pr 的第j个子女,*p与*pr,右兄弟*q一起调整
    BTNode  *q = pr->ptr[j+1];
    int k;    //*p的右兄弟
    if(q->n > d-1){
    
    //右兄弟空间够,做移动
        p->key[p->n+1] = pr->key[j+1];//双亲结点相应关键码下移
        pr->key[j+1] = q->key[1]; //右兄弟关键码上移
        p->ptr[p->n+1] = q->ptr[0]; //右兄弟最左指针左移
        if(q->ptr[0] != NULL) q->ptr[0]->parent = p;
        for(k=1;k<=q->n;k++) q->ptr[k-1] = q->ptr[k];
        for(k=2;k<=q->n;k++) q->key[k-1] = q->key[k];
        p->n++;
        q->n--;
        return false;
    }else{
    
    
     merge(p,pr,q,j);
     return true; //p与q合并
    }
}
bool RightAdjust(BTNode *p,BTNode *pr,int d,int j){
    
    
    //*p是其双亲*pr 的第j个子女,*p与*pr,左兄弟*q一起调整
    BTNode  *q = pr->ptr[j-1];
    int k;    //*p的左兄弟
    if(q->n > d-1){
    
    //左兄弟空间够,做移动
        for(k=p->n;k>=0;k--) p->ptr[k+1] = p->ptr[k];
        for(k=p->n;k>=1;k--) p->key[k+1] = p->key[k];
        p->ptr[0] = q->ptr[q->n];//左兄弟最后指针右移
        if(q->ptr[q->n] != NULL) q->ptr[q->n]->parent = p;
        p->key[1] = pr->key[j]; //双亲相应关键码下移
        pr->key[j] = q->key[q->n];//左兄弟最后关键码上衣移
        q->n--;
        p->n++;
        return false;
    }else{
    
    
        merge(q,pr,p,j-1);
        return true; //p与q合并
    }
}

bool Remove(BTree &T,KeyType k){
    
    
    //算法调用方式bool succ = remove(T,k)
    //输入B树的根指针t,删除关键码k
    //成功true,错误false
    int i,j,d = (m+1)/2;
    BTNode *p,*pr,*s;
    bool succ;
    if(!Search(T,k,p,i)) return false; //查找k失败,返回
    if(p->ptr[i]!= NULL){
    
      //若p是非叶节点
        s = p->ptr[i];
        pr = p;  //查找K[i]的右子树左下节点pr
        while(s!=NULL) {
    
    
            pr = s;
            s = s->ptr[0];
        }
        p->key[i] = pr->key[1];//用此结点最小关键码寻找
        for(j=2;j<=pr->n;j++){
    
    
            pr->key[j-1] = pr->key[j];
            pr->ptr[j-1] = pr->ptr[j];
        }
        pr->n--; //叶结点关键码个数减1
        p = pr; //下一步处理q结点中的删除
    }else{
    
    
        for(j = i+1;j<=p->n;j++){
    
    
            p->key[j-1] = p->key[j];
            p->ptr[j-1] = p->ptr[j];
        }
        p->n--; //叶节点关键码个数减1
    }
    while(1){
    
    
        if(p->n < d-1){
    
    
            pr = p->parent;
            for(j=0;j<=pr->n && pr->ptr[j] != p;j++);
            if(j==0)
                succ = LeftAdjust(p,pr,d,j);
            else
                succ = RightAdjust(p,pr,d,j);
            if(succ){
    
      //继续向上做节点调整工作
                p = pr;
                if(p==T) break;
            }
        }else break;    //不小于d-1,无需调整,跳出
    }
    if(T->n == 0){
    
       //当根节点为空删除节点
        p = T->ptr[0];
        free(T);
        T = p;
        T->parent = NULL;
    }
    return true;
}

void count_Keynum(BTree T,int& count){
    
    
    if(T != NULL){
    
    
        count = count+ T->n;
        for(int i =0;i<=T->n;i++)  //累加关键码个数
            count_Keynum(T->ptr[i],count);//进入子树累加
    }
}

void Traversal(BTree T){
    
    
    if(T!=NULL){
    
    
        Traversal(T->ptr[0]);
        for(int i =1;i<=T->n;i++){
    
    
            printf("%d ",T->key[i]);
            Traversal(T->ptr[i]);
        }
    }

}
int main(){
    
    
    BTree  T=NULL;
    int a[11]={
    
    2,3,4,5,6,7,8,9,10,11,12};
//    Insert(T,a[0]);
    for(int i =0;i<11;i++){
    
    
        Insert(T,a[i]);
    }
    Traversal(T);

    return 0;

}

猜你喜欢

转载自blog.csdn.net/m0_37149062/article/details/124075546