B树的基本例程(1)插入

摘要:B树的基本定义:每个节点x都有以下域:
a)n[x],当前存储在节点x中的关键字数
b)n[x]个关键字本身,以非降序存放;
c)leaf[x],是一个布尔值,标志是否是叶子.
(2)每个节点内还包含n[x]+1个指向其子女的指针;
(3)每个节点都具有相同的深度h。
(4)每个节点能包含的关键字数有一个上界和下界,这些界可以用一个称作B数的最小度数的固定整数t来表示.t>=2;
(5)每个非根节点必须至少有t-1个关键字,每个非根节点内至少有t个子女。
(6)每个节点至多包含2t-1个关键字,所以一个内节点至多有2t个子女;
基本操作
(1)搜索B树,搜索B树与搜索二叉查找树很相似,只是在每个节点所做的不是个二叉搜索,而是由该节点的子女数决定的.基本思路是遍历该节点的所有关键字,直到要搜索的元素x小于等于某个关键字或者大于所有关键字.
然后判断要搜索的元素就等于临界关键字,或者小于它.或者大于所有关键字.如果该节点是叶子,则没有子女,否则根据情况判断是否继续向下搜索.(这一部分的代码将直接运用到删除例程中)
(2)插入数据:因为B树的特点,因此必须将新的数据插入到一个叶子节点中.但是又不能将关键字插入到满叶子节点,故引入一个分裂操作,将一个满的节点从中间分裂成两个各含t-1个关键字的节点,而中间关键字则被提升到父节点.如果父节点也是满的,则它必须在关键字被插入前分裂.因此在查找过程中,遇到满节点就分裂,这样可以保证每分裂一个满节点时它的父节点不是满的。

int maxIndex(Position p){ 
        int i=0; 
        for(;i<2*DU;i++){ 
                if(p->data[i]==0) 
                        return i-1;   //如果遇到了0说明前一个下标是最大的下标
        } 
        return 2*DU-2;           //如果到了这里说明该节点是满节点,最大关键字下标是2*DU-2
}
void shiftLeft(Position p,int start,int offset)
{      //start是开始移动的位置,offset 移动的位移
        int tmp=start; 
        int max=maxIndex(p); 
        if(max==0) 
                max=1; 
        for(;tmp<=2*DU-2;tmp++)
        {                 //左移关键字
                p->data[tmp]=p->data[tmp+offset]; 
                p->data[tmp+offset]=0;       //将无用的关键字置为0
        } 
        tmp=start; 
        for(;tmp<=2*DU-1;tmp++)
        { 
                p->child[tmp]=p->child[tmp+offset]; //左移子节点指针 
                p->child[tmp+offset]=NULL;          //将无用的子节点指针置为NULL
        } 
}

void shiftRight(Position p,int start)
{ 
       //向右移动一位
        int max=maxIndex(p); 
        int index=max; //index 是key的下标
        for(;index>=start;index--)
                p->data[index+1]=p->data[index]; //右移关键字数组 
        for(index = max + 1;index>=start;index--)
            p->child[index+1] = p->child[index];
}
Position splitPage(Position p,int val)
{
         //p节点为需要分裂的满节点,val是用来决定返回递归的新节点是B节点(原来的节点)还是新分裂的C节点
        int mid=p->data[DU-1];        //取得p中间位置的节点,它被分裂到父节点中 
        Position right = pm();             //新建C节点
        Position result;
        int index=DU;                        
        int i=0;
        for(;index<=2*DU-2;index++)
        {  
                //开始复制B节点的关键字数组和子节点指针数组,
                right->data[i]=p->data[index];
                p->data[index]=0;
                right->child[i]=p->child[index];
                p->child[index]=NULL;
                if(right->child[i]!=NULL)
                        right->child[i]->parent=right;
 //B节点的一部分子节点迁移到了C节点上,所以要重新设置子节点的父节点指针
                i++;
        }
        right->child[i]=p->child[2*DU-1];//子节点指针数组比关键字数组个数多一个
        if(right->child[DU-1]!=NULL)
           right->child[DU-1]->parent=right; //同时设置最后一个子节点指针的父节点
        p->child[2*DU-1]=NULL;
        p->data[DU-1]=0;
        right->isLeaf=p->isLeaf;                  //设置C节点是否是叶子节点, 
        if(p->parent==NULL)
        {
            //如果p节点就是根节点了,那么需要新生成Root节点
             Position  root = pm();
                p->parent=root;
                root->isLeaf=0;
        }
        Position parent=p->parent;
        int first=0;
        for(;first<= 2*DU-2;first++){//取得父节点中mid可插入的位置
                if(parent->data[first]>mid||parent->data[first]==0)
                        break;
        }
        index=2*DU-3;
        for(;index>=first;index--)
        {
                //关键字和子节点指针数组右移1位
                parent->data[index+1]=parent->data[index];
                parent->child[index+2]=parent->child[index+1];
        }
        parent->data[first]=mid;  //插入新的关键字mid,和新的子节点指针right
        parent->child[first+1]=right;
        parent->child[first] = p;//避免生成新节点第一个子节点信息缺少
        right->parent=parent;              
        result = val > mid ? right:p;  //确定返回的节点
        return result;
}

void InsertData(Position p,int val)
{ 
    //p表示目的节点,val表示要插入的值
        if(pageSize(p)==2*DU-1){  //如果递归的过程中碰到满节点,就先分裂p节点
              p=splitPage(p,val); //分裂的过程后,重新设置p节点
        } 
        int index=0; 
        for(;index<sizeof(p->data)/sizeof(int);index++)
        { //寻找子节点的位置,因为分裂节点保证了p节点不是满节点,所以不用考虑满节点的情况(满节点的话index需要加1)
                if(p->data[index]==0||p->data[index]>val){ 
                                 break; 
                } 
        } 
        if(p->isLeaf==0)
        {
            //如果不是叶子节点,递归插入到子节点 
          InsertData(p->child[index],val); 
            return ;
        }
        if(pageSize(p)>0) //如果节点中有关键字则右移1个位置
        shiftRight(p,index); 
        p->data[index]=val; //把新的关键字插入到相应的位置
        return;     
}
    原文作者:B树
    原文地址: https://blog.csdn.net/pp634077956/article/details/48153445
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞