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;
}