数据结构基础-B树的实现

本文详细介绍了B树这种自平衡数据结构,包括其定义、节点创建与销毁、子树分裂与归并、插入与删除操作,强调了在保持数据有序的同时,B树在数据库和文件系统中的应用。通过理解B树的内部工作机制,有助于实现高效的数据管理。
摘要由CSDN通过智能技术生成

B树是非常重要的数据结构,它是一种自平衡树, 能够保持数据有序。并且这种结构可允许查找数据、顺序访问、插入数据以及删除数据。这种数据可以用来描述外部存储。该结构经常用在数据库文件系统的实现上。
B树有如下3条规则:

  1. B树中每个节点的元素数量和子树的数量都是有限的,除了根节点外,所有节点最多拥有M-1个元素,所有非叶子非根节点最多拥有M个子树(称为M阶B树)
  2. 根节点至少拥有两个子树,除了根节点之外的非叶子节点拥有K个子树以及K-1个元素((M+1/2) < K < M),元素按照递增或递减顺序排列
  3. 所有叶子节点属于同一层

现在我将一一介绍B树的源码,来讲解B树结构的过程。

1. B树和B树节点的定义

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
​
typedef int KEY_VALUE;
​
typedef struct _btree_node
{
    KEY_VALUE *keys;
    struct _btree_node **childrens;
    int num;
    int leaf;
} btree_node;

// definition of b-tree
// root: the pointer to the root node, t: minimum degree
typedef struct _btree {
    btree_node *root;
    int t;
} btree;

首先,我们定义B树的节点为结构体_btree_node。成员如上面的代码片段所示。keys是一堆索引,KEY_VALUE 表示key的数据类型。实际编译的时候,KEY_VALUE会将该变量定义为int类型。childrens为子节点序列。num*keys数组中有效子节点的个数, leaf判断该节点是否是叶子节点的变量。

有了节点定义之后,我们就可以定义整棵树_btree。在上面的代码片段里,我们在树结构体内定义树的根节点为root, 以及树的最小阶(degree)t

2. B树节点的创建和B树的创建

static btree_node *btree_create_node(int t, int leaf) {
    // create a node in a b-tree
    btree_node *node = (btree_node *)calloc(1, sizeof(btree_node));
    if (node == NULL) return NULL;
    
    node->leaf = leaf;
    node->keys = (KEY_VALUE *)calloc(1, (2*t-1)*sizeof(KEY_VALUE));
    if (node->keys == NULL) {
        free(node);
        return NULL;
    }
​
    node->childrens = (btree_node **)calloc(1, (2*t) * sizeof(btree_node*));
    if (node->childrens == NULL) {
        free(node->keys);
        free(node);
        return NULL;
    }
    node->num = 0;
​
    return node;
}

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

接下来,有了B树的定义,我们可以定义如何创建B树的节点以及B树。首先,对于创建B树的节点的创建,我们传递参数t和leaf。t就是被创建的节点的编号,leaf就是被创建节点是否是叶节点。1就是叶节点,0就是内节点。节点一共有4个变量,leaf,keys,childrens以及num。leaf直接从传参的leaf获取值;key代表的索引编号,并赋予初始空间(2*t-1)*sizeof(KEY_VALUE)2*t-1就是上述定义中的多元素个数(2t阶子树)。接着,给childrens变量分配空间(2*t)*sizeof(btree_node*)。同样也是由定义给出的。然后我们node的数目为num。因为是初始化节点,所以key的有效子节点*个数是0,进而num=0

我们除了要创建树的节点,更不要忘记创建树!关于树的创建,我们只需要给树结构体赋予变量t和创建树的根节点。源码实现如上所示。

3. B树节点的销毁和B树的销毁

#节点的销毁
void btree_destroy_node(btree_node *node) {
​
    assert(node);
​
    free(node->childrens);
    free(node->keys);
    free(node);
​
}

#树的销毁
void btree_destroy(btree *T) {
    if (!T->root) {
        return;
    }
    btree_delete_tree(T->root);
    T->root = NULL;
}

static void btree_delete_tree(btree_node *node) {
    if (node->leaf) {
        btree_destroy_node(node);
        return;
    }
​
    int i = 0;
    for (i = 0; i <= node->num; i++) {
        btree_delete_tree(node->childrens[i]);
    }
    btree_destroy_node(node);
}
  1. btree_destroy_node:删除节点node,不仅仅需要将节点删除,我们还需要把节点里面给childrenskeys分配的空间也要free掉。如果需要把代码写的更鲁棒(robust)一点,我们使用断言函数assert()判断需要删除的节点node是否存在。

  2. btree_destroy and btree_delete_tree。关于删除树的实现,稍微有点复杂。我们可以采用递归的方法实现。具体来说,由于B树都是由高层节点指向底层节点,直到指向叶节点。所以,我们删除的顺序,是自底向上,从叶节点开始删除。我先实现了递归删除函数btree_delete_tree。在这个函数里,当节点是叶节点,则用节点删除函数btree_destroy_node来删除,然后回归上一轮函数。否则,我们会对这个node的每一个子节点做递归删除遍历。遍历结束后,我们才能够把这个节点删除。

实现该函数后,则可以写出删除树的函数。简单来说就是对树的根节点做递归删除即可。

4. B树子树的分裂。

​
static 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--) {
  
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值