B树可以是多叉树多叉树不一定是多叉树
多叉树和b树的差别
多叉树说明树的结构有多个叉,而B树的所有叶子节点在同一层.
B树的性质:
B树的层高:
B树
B树的所有结点都存储数据 内存寻址用红黑和B
B+树
B+树的内结点不存储数据,仅作索引;所有数据都是存储在叶子结点上。 而B树的所有结点都是存储数据的。 B+树更适合做磁盘索引,性能优于B树;因为B+的内结点不存储数据。 磁盘寻址用B+ 更适合做磁盘索引,内节点就是非叶子节点,右子节点
B树的节点定义:
typedef int KEY_VALUE;
//n+1叉树 key = n个
typedef struct _btree_node {
KEY_VALUE *keys; //n个
struct _btree_node **childrens; //分n+1叉
int num; //判断节点中存储了多少个数据
int leaf; //判断是否是叶子节点
} btree_node;
B树的根节点定义:
typedef struct _btree {
btree_node *root;
int t;
} btree;
B树的添加
根节点分叉
key为奇数 分叉数量/子树数量是偶数 key为奇数是为了方便分裂的时候将中间节点提出
当满足子节点到五个时 开始分裂将中间的F放到和C并列
注意:是先分裂再添加这样不影响性质
一直添加到根节点也满足5
每次添加叶子节点 如何满足最大值之后下一次添加将中间的节点提拔为父节点 以此往复
B树的叶子节点删除
在添加完全的基础上进行删除
先查找 查找的时间复杂度是log n(m/n)
合并删除
先找到A 不能直接删因为会违背性质 因为A小于C所以合并C两侧的子树进行删除
合并后借位分裂
只能从父节点借位 借不到就采用合并删除
问题:在什么时候需要借位
两边子树节点数量不平衡
如果子树的数量 = m/2 -1 这时候就需要借位 避免之后资源不足
总结:对于B树的删除操作先通过合并或者借位使得B树能够达到删除的状态再进行删除操作
需要子树数量满足M/2 - 1 > 2 才能删除
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);
node->leaf = leaf;
node->keys = (KEY_VALUE*)calloc(1, (2*t-1)*sizeof(KEY_VALUE)); //calloc是自动清0 malloc需要手动清空
node->childrens = (btree_node**)calloc(1, (2*t) * sizeof(btree_node*));
node->num = 0;
return node;
}
删除树
void btree_destroy_node(btree_node *node) {
assert(node);
free(node->childrens);
free(node->keys);
free(node);
}
分裂操作
//第一个参数是树,第二个参数是分裂的哪个节点,第三个参数是从第几个开始分
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;
//提拔x为内节点 将x插到中间
for (j = x->num;j >= i+1;j --) {
x->childrens[j+1] = x->childrens[j];
}
x->childrens[i+1] = z;
//对x的key进行交换
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;
}
添加节点
创建空的节点然后指向
void btree_insert(btree *T, KEY_VALUE key) {
//int t = T->t;
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], key);
} else {
btree_insert_nonfull(T, r, key);
}
}
合并
//{child[idx], key[idx], child[idx+1]}
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;
/data merge
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);
}
}