B树和B+树

B-树即B树,B树又叫平衡多路查找树,一颗m阶的B树(m叉树)的特性如下:
1.树中的每个节点最多含有m个孩子(m >=2)
2.除根节点和叶子节点以外,其他每个节点至少有【ceil(m/2)】个孩子
3.若根节点不是叶子节点,则至少有2个孩子
4.所有的叶子节点都出现在同一层,叶子节点不包含任何关键字的信息
5.每个非终端节点中包含n个关键字信息:(n,P0,K1,P1,K2,P2,…,Kn,Pn)。其中:
a) Ki (i=1…n)为关键字,且关键字按顺序升序排序K(i-1)< Ki。
b) Pi为指向子树根的接点,且指针P(i-1)指向子树种所有结点的关键字均小于Ki,但都大于K(i-1)。
c) 关键字的个数n必须满足: [ceil(m / 2)-1]<= n <= m-1。如下图所示:
在这里插入图片描述
话不多说,开始b树的编码。
首先开始定义一个b树节点

typedef int KEY_VALUE;
typedef struct _btree_node {
    //定义节点的关键字
    KEY_VALUE *key    s;
    //定义节点的子节点
    struct _btree_node **childrens;
    //关键子的个数
    int num;
    //是否时叶子节点
    int leaf;
}btree_node;

定义一棵b树

typedef struct _btree {
    //根节点
    struct btree_node * root;
    //多少阶B树
    int t;
}btree;

创建一个b树节点:

btree_node *btree_create_node(int t, int leaf) {
    btree_node *node = (btree_node *)calloc(1, sizeof(btree_node));
    if(node == NULL) {
        assert(0);
    }
    
    //返回连续2*t的btree_ndoe内存,这里一开始就把所有内存就分配好了
    node->childrens = (btree_node**)calloc(1, sizeof(btree_node)*(2 * t));
    node->num = 0;
    node->keys = (KEY_VALUE *)calloc(1, sizeof(KEY_VALUE)*(2*t -1));
    node->leaf = leaf;
    
    return node;
    
}

释放一个b树节点:

btree_node *btree_destory_node(btree_node *node){
    assert(node);
    
    free(node->childrens);
    free(node->keys);
    free(node);
}

创建一个树:

void btree_create(btree *T, int t){
    T->t = t;
    btree_node *x = btree_create_node(t, 1);
   
    T->root = x;
}

插入一个节点
节点分裂

/*******
 B树中的x节点的i子节点进行分裂
********/
void btree_split_child(btree *T, btree_node *x, int i){
    int t = T->t;
    
    btree_node* y = x->childrens[i];
    //准备分裂的点
    btree_node* z = btree_create_node(t, y->leaf);
    
    int j = 0;
    for(j = 0; j < t-1; t++){
        //把y的后面t-1 key值付给分裂节点z
        z->keys[j] = y->keys[t+j];
    }
    
    if(y->leaf == 0){
        int j = 0;
        for(j=0; j < t; j++){
            //把y后面的t个子节点复制给z
            z->childrens[j] = y->childrens[j+t];
        }
    }
    
    y->num = t - 1;
    
    for(j = x->num -1; j >= i +1; j--) {
        //需要插入新的子节点
        x->childrens[j + 1] = x->childrens[j];
    }
    
    x->childrens[i + 1] = z;
    
    for(j= x->num -1; j >= i; j--){
        //由于x需要在i几点插入一个新的关键字,所以
        //需要i节点后的关键字要向后迁移
        x->keys[j+1] = x->keys[j];
    }
    x->keys[i] = y->keys[t - 1];
    x->num++;
}

递归插入一个k值

void btree_insert_nonfull(btree *T, btree_node *x, KEY_VALUE k) {
    int i = x->num - 1;
    //如果时叶子节点进行插入
    if(x->leaf == 1){
        //先找好对应的坑
        while(i >= 0 && x->keys[i] > k) {
            x->keys[i + 1] = x->keys[i];
            i--;
        }
        //插入值,并且对应数目+1
        x->keys[i + 1] = k;
        x->num++;
    } else {
        //不是叶子节点
        while(i >= 0 && x->keys[i] > k) {
            i--;
        }
        //判断对应的坑的子节点的关键值个数是不是2t - 1
        if(x->childrens[i + 1]->num == (2*(T->t) - 1)){
            btree_split_child(T, x, i+1);
            if(k > x->keys[i+1]){
                i++;
            }
        }
        //递归到子节点进行插入
        btree_insert_nonfull(T, x->childrens[i+1], k);
    }
}

插入一个节点

void btree_insert(btree *T, KEY_VALUE k){
    
    //获取根节点
    btree_node *r = T->root;
    if(r->num == (2 * (T->t) - 1)){
        //根节点满的情况,先创建一个新的节点,然和把根节点设置为新创建的点
        //最后把原来的根节点最为新节点的第一个子节点
        btree_node *node = btree_create_node(T->t, 0);
        T->root = node;
        node->childrens[0] = r;
        
        //对新根节点的子节点进行分裂
        //然后在对应的坑为进行插入操作
        btree_split_child(T, node, 0);
        int i = 0;
        if(node->keys[0] < key) {
            i++;
        }
        btree_insert_nonfull(T, node->childrens[i], k);
    } else {
        //如果根节点不满的话直接进行插入
        btree_insert_nonfull(T, r, k);
    }   
}

遍历平衡树

void btree_traverse(btree_node *x){
    int i = 0;
    for(i=0; i < x->num; i++){
        if(x->leaf == 0)
            btree_traverse(x->childrens[i]);
        printf("%C ", x->keys[i]);
    }
    if(x->leaf == 0){
        //num个关键字,对应num + 1chiled
        //所以要多遍历一个
        btree_traverse(x->childrens[i]);
    }
}

B树节点的二分查找
为了加快key在节点中的快速查找,这里采用二分法

int btree_bin_search(btree_node *node, int low, int high, KEY_VALUE key){
    int mid;
    if(low > high || low < 0 || high < 0){
        return -1;
    }
    
    while(low <= high){
        mid = (low + high) / 2;
        if(node->keys[mid] > key) {
            high = mid -1;
        } else if(node->keys[mid] < key){
            low = mid + 1;
        } else{
            return mid;
        }
    }
    return low;
}

删除节点

void btree_merge(btree *T, btree_node *node, int idx){
    btree_node* left = node->childrens[idx];
    btree_node* right = node->childrens[id + 1];
    
    //left的节点不可能小于T-t -1,所以合并的时候把node idx值放在left->keys[T->t - 1]
    left->keys[T->t - 1] = node->keys[idx];
    for(i=0; i < T->t - 1; i++){
        left->keys[T->t + i] = right->keys[i];
    }
    
    if(!left->leaf){
        for(i=0; i < T->t; i++){
            left->childrens[T->t+i] = right->childrens[i];
        }     
    }
    
    left->num += T->t;
    //destroy right
    btree_destroy_node(right);
    
    //node 
    for (i = idx+1;i < node->num;i ++) {
        node->keys[i-1] = node->keys[i];
        node->childrens[i] = node->childrens[i+1];
    }
    node->childrens[i+1] = NULL;
    node->num -= 1;
    
    if (node->num == 0) {
        T->root = left;
        btree_destroy_node(node);
    }
   
}


void btree_delete_key(btree *T, btree_node *node, KEY_VALUE key){
    if(node == NULL){
        return;
    }
    
    int id = 0, i;
    
    //找到node节点中第一个不小于key的关键字位置
    while(id < node->num && node->keys[id] < key) {
        id++;
    }
    
    if(id < node->num && node->keys[id] == key){
        //key值在此节点上
        if(node->leaf){
            for(i=idx; i < node->num -1; i++){
                node->keys[i] = node->keys[i+1];
            }
            node->keys[node->num - 1] = 0;
            node->num--;
            
            if(node->num == 0){
                free(node);
                T->root = NULL;
            }
            return;
        }else if(node->childrens[id]->num >= T->t){
            btree_node *left = node->childrens[id];
            node->keys[id] = left->keys[left->num - 1];
            btree_delete_key(T, left, left->keys[left->num - 1]);
        }else if(node->childrens[id + 1]>= T->t){
            btree_node *right = node->childrens[id + 1];
            node->keys[id] = right->keys[0];
            btree_delete_key(T, right, right->keys[0]);
        } else{
            btree_merge(T, node, id);
            btree_delete_key(T, node->childrens[id], key);
        }
    }else{
        //不在该节点上,这里是否可以用递归来做
        btree_node *child = node->childrens[idx];
        if (child == NULL) {
            printf("Cannot del key = %d\n", key);
            return ;
        }
        
        if (child->num == T->t - 1){
            btree_node *left = NULL;
            btree_node *right = NULL;
            if (idx - 1 >= 0)
                left = node->childrens[idx-1];
            if (idx + 1 <= node->num) 
                right = node->childrens[idx+1];
            if ((left && left->num >= T->t) ||
                    (right && right->num >= T->t)) {
                int richR = 0;
                if (right) richR = 1;
                if (left && right) richR = (right->num > left->num) ? 1 : 0;
                
                if (right && right->num >= T->t && richR) { //borrow from next
                        child->keys[child->num] = node->keys[idx];
                        child->childrens[child->num+1] = right->childrens[0];
                        child->num ++;


                        node->keys[idx] = right->keys[0];
                        for (i = 0;i < right->num - 1;i ++) {
                                right->keys[i] = right->keys[i+1];
                                right->childrens[i] = right->childrens[i+1];
                        }


                        right->keys[right->num-1] = 0;
                        right->childrens[right->num-1] = right->childrens[right->num];
                        right->childrens[right->num] = NULL;
                        right->num --;
                                                        
                } else { //borrow from prev


                        for (i = child->num;i > 0;i --) {
                                child->keys[i] = child->keys[i-1];
                                child->childrens[i+1] = child->childrens[i];
                        }


                        child->childrens[1] = child->childrens[0];
                        child->childrens[0] = left->childrens[left->num];
                        child->keys[0] = node->keys[idx-1];
                        
                        child->num ++;


                        node->keys[idx-1] = left->keys[left->num-1];
                        left->keys[left->num-1] = 0;
                        left->childrens[left->num] = NULL;
                        left->num --;
                }
            }
            
        }else if ((!left || (left->num == T->t - 1))
          && (!right || (right->num == T->t - 1))) {


              if (left && left->num == T->t - 1) {
                      btree_merge(T, node, idx-1);					
                      child = left;
              } else if (right && right->num == T->t - 1) {
                      btree_merge(T, node, idx);
              }
      }
        
        btree_delete_key(T, child, key);
        
    }
}

int btree_delete(btree *T, KEY_VALUE key){
    if(T->root == NULL){
        return -1;
    }
    btree_delete_key(T,T->root,key);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值