B/B+树的介绍(理解应用场景,思想即可,代码只作参考)

应用场景:

案例一:设计一个图片存储索引组件
1. 图片的数量是巨大的。
2. 可以添加图片,可以删除图片
3. 可以通过图片名称进行查找
4. 添加,删除,查找都是对单一图片进行操作

一般应用于磁盘管理,数据库的索引

定义:

一颗M阶B树T,满足以下条件
1. 每个结点至多拥有M颗子树
2. 根结点至少拥有两颗子树
3. 除了根结点以外,其余每个分支结点至少拥有M/2课子树
4. 所有的叶结点都在同一层上
5. 有k课子树的分支结点则存在k-1个关键字,关键字按照递增顺序进行排序
6. 关键字数量满足ceil(M/2)-1 <= n <= M-1

Btree:所有结点储存数据

B+tree:叶子结点存储数据,内结点索引用。(数据库的索引用了b+树的原理)

B树删除结点

先合或者借 借不到再删

根结点分裂:一个分裂成三个


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

// 定义度数为3
#define DEGREE		3
// 使用整数表示键值
typedef int KEY_VALUE;

// 定义B树节点结构体
typedef struct _btree_node {
	KEY_VALUE *keys; // 存储键值的数组
	struct _btree_node **childrens; // 存储子节点指针的数组
	int num; // 节点中键值的数量
	int leaf; // 标记是否为叶子节点
} btree_node;

// 定义B树结构体
typedef struct _btree {
	btree_node *root; // B树的根节点
	int t; // B树的最小度数
} btree;

/**
 * 创建B树节点
 * @param t B树的最小度数
 * @param leaf 是否为叶子节点
 * @return 新创建的节点指针
 */
btree_node *btree_create_node(int t, int leaf) {

	btree_node *node = (btree_node*)calloc(1, sizeof(btree_node));
	if (node == NULL) assert(0);

	node->leaf = leaf;
	node->keys = (KEY_VALUE*)calloc(1, (2*t-1)*sizeof(KEY_VALUE));
	node->childrens = (btree_node**)calloc(1, (2*t) * sizeof(btree_node*));
	node->num = 0;

	return node;
}

/**
 * 销毁B树节点
 * @param node 待销毁的节点指针
 */
void btree_destroy_node(btree_node *node) {

	assert(node);

	free(node->childrens);
	free(node->keys);
	free(node);
	
}

/**
 * 创建B树
 * @param T 待创建的B树指针
 * @param t B树的最小度数
 */
void btree_create(btree *T, int t) {
	T->t = t;
	
	btree_node *x = btree_create_node(t, 1);
	T->root = x;
	
}

/**
 * 分裂节点的子节点
 * @param T B树指针
 * @param x 待分裂的节点指针
 * @param i 子节点在x的子节点数组中的索引
 */
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);

	z->num = t - 1;

	int j = 0;
	for (j = 0;j < t-1;j ++) {
		z->keys[j] = y->keys[j+t];
	}
	if (y->leaf == 0) {
		for (j = 0;j < t;j ++) {
			z->childrens[j] = y->childrens[j+t];
		}
	}

	y->num = t - 1;
	for (j = x->num;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->keys[j+1] = x->keys[j];
	}
	x->keys[i] = y->keys[t-1];
	x->num += 1;
	
}
// 将一个键插入到B树节点中(非满状态)
void btree_insert_nonfull(btree *T, btree_node *r, KEY_VALUE key);
/**
 * 遍历B树并打印所有键
 * @param x 当前遍历的节点
 */
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) btree_traverse(x->childrens[i]);
}

/**
 * 打印B树结构
 * @param T B树
 * @param node 当前打印的节点
 * @param layer 当前节点所在的层次
 */
void btree_print(btree *T, btree_node *node, int layer) {
    btree_node* p = node;
    int i;
    if(p) {
        printf("\nlayer = %d keynum = %d is_leaf = %d\n", layer, p->num, p->leaf);
        for(i = 0; i < node->num; i++)
            printf("%c ", p->keys[i]);
        printf("\n");
#if 0
        printf("%p\n", p);
        for(i = 0; i <= 2 * T->t; i++)
            printf("%p ", p->childrens[i]);
        printf("\n");
#endif
        layer++;
        for(i = 0; i <= p->num; i++)
            if(p->childrens[i])
                btree_print(T, p->childrens[i], layer);
    } else printf("the tree is empty\n");
}

/**
 * 在B树节点中进行二分查找
 * @param node 当前节点
 * @param low 查找范围下限
 * @param high 查找范围上限
 * @param key 要查找的键
 * @return 键的位置索引或-1(未找到)
 */
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 (key > node->keys[mid]) {
            low = mid + 1;
        } else {
            high = mid - 1;
        }
    }
    return low;
}

/**
 * 合并两个子节点
 * @param T B树
 * @param node 当前节点
 * @param idx 子节点索引
 */
void btree_merge(btree *T, btree_node *node, int idx) {
    btree_node *left = node->childrens[idx];
    btree_node *right = node->childrens[idx+1];
    int i = 0;
    // 合并数据
    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;
    // 释放右侧子节点
    btree_destroy_node(right);
    // 更新父节点
    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);
    }
}

/**
 * 删除B树中的键
 * @param T B树
 * @param node 当前节点
 * @param key 要删除的键
 */
void btree_delete_key(btree *T, btree_node *node, KEY_VALUE key) {
    if (node == NULL) return;
    int idx = 0, i;
    // 寻找键的位置
    while (idx < node->num && key > node->keys[idx]) {
        idx++;
    }
    if (idx < node->num && key == node->keys[idx]) {
        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[idx]->num >= T->t) {
            // 如果子节点足够多,从子节点中借一个键
            btree_node *left = node->childrens[idx];
            node->keys[idx] = left->keys[left->num - 1];
            btree_delete_key(T, left, left->keys[left->num - 1]);
        } else if (node->childrens[idx+1]->num >= T->t) {
            // 如果下一个子节点足够多,从下一个子节点中借一个键
            btree_node *right = node->childrens[idx+1];
            node->keys[idx] = right->keys[0];
            btree_delete_key(T, right, right->keys[0]);
        } else {
            // 合并子节点,并递归删除键
            btree_merge(T, node, idx);
            btree_delete_key(T, node->childrens[idx], 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) {
                    // 从下一个子节点借一个键
                    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 {
                    // 从上一个子节点借一个键
                    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->key[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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值