1.2磁盘存储链式的B树与B+树

B树和B+树是平衡树的一种,在数据库、文件系统等大规模存储系统中广泛应用,主要用于实现高效的查找、插入和删除操作。它们都具有自平衡的特性,能够在最坏情况下提供对数时间复杂度的操作。

1. B树(B-Tree)
定义

B树是一种自平衡的多路查找树,它将数据存储在节点中并按顺序排列。B树的每个节点可以包含多个子节点和多个元素,因此它在存储大量数据时非常高效。

性质
  • 每个节点可以有多个子节点:B树的每个节点不仅有一个数据项,而且可以有多个子节点,允许节点有多个键值。
  • 节点的键值按顺序排列:节点内部的键值是有序的,从左到右逐渐增大。
  • 根节点至少有两个子节点(除非是空树),其他节点至少有 ⌈m/2⌉ 个子节点,其中 m 是树的阶数。
  • 节点的键数限制:一个节点最多包含 m - 1 个键,最少包含 ⌈m/2⌉ - 1 个键。
  • 查找效率:B树的查找操作从根节点开始,依次向下查找子树,时间复杂度为 O(log n),其中 n 为树中的元素个数。
  • 插入与删除操作:插入和删除操作保持树的平衡,当节点满时,会分裂节点;当节点键值过少时,会进行合并操作。
  • 树的高度相对较低:由于每个节点包含多个键,B树的高度通常比较低,因此查找和操作效率较高。
适用场景
  • 数据库索引:B树适用于大规模存储数据,广泛应用于数据库和文件系统中的索引结构。
  • 磁盘存储:由于B树具有较高的存储密度和较少的树高,它非常适合磁盘存储系统,减少了磁盘I/O操作的次数。
cpp 复制代码
#define SUB_M    3


typedef struct _btree_node {
    
    int *keys; // int keys[2 * SUB_M - 1];  5
    struct _btree_node **childrens; //struct _btree_node *childrens[2 * SUB_M - 1]; 6
    
    int num;
    int leaf;
    
}btree_node;


typedef struct _btree {
    
    struct _btree_node *root;
}
2. B+树(B+ Tree)
定义

B+树是B树的变种,B+树的所有值都存在叶子节点,并且叶子节点通过链表连接。它与B树的不同之处在于,B+树的内部节点只存储键值,不存储实际数据,所有数据只存在叶子节点。

性质
  • 所有值都存在叶子节点:B+树的非叶子节点只包含键值,实际的数据值只存储在叶子节点中。
  • 叶子节点通过链表连接:所有叶子节点组成一个链表,这使得B+树的范围查询非常高效。可以通过叶子节点之间的链表快速访问范围内的数据。
  • 内部节点只存储键值:B+树的非叶子节点只存储键值信息,并不存储数据,简化了节点的存储结构。
  • 与B树相比,B+树的查询效率较高:因为数据存储在叶子节点并且叶子节点形成链表,所以在范围查询时,B+树可以通过链表进行顺序访问,提高了范围查询的效率。
  • 插入与删除:B+树的插入和删除操作与B树类似,都是通过节点分裂和合并来保持树的平衡。由于内部节点不存储数据,B+树的插入和删除操作通常较为高效。
  • 树的高度:B+树的高度通常和B树相同,因为它们的阶数和高度结构是一样的。
适用场景
  • 数据库索引:B+树是现代数据库管理系统中最常用的索引结构,因为它在范围查询和顺序扫描方面更具优势。常见的数据库系统如 MySQL、Oracle 等都使用 B+ 树作为索引结构。
  • 文件系统:文件系统中的目录结构、文件的查找、顺序读取等操作通常采用 B+ 树来组织数据。
  • 磁盘和SSD存储:由于数据和指针的分离,B+树在磁盘或SSD上能够更有效地减少I/O操作,适合大规模的数据存储和访问。
3.B-树的删除

先合并在删除

先借位在删除

cpp 复制代码
#define SUB_M    3


typedef struct _btree_node {
    
    int *keys; // int keys[2 * SUB_M - 1];  5
    struct _btree_node **childrens; //struct _btree_node *childrens[2 * SUB_M - 1]; 6
    
    int num;
    int leaf;
    
}btree_node;


typedef struct _btree {
    
    struct _btree_node *root;
}btree;


// 创建结点
btree_node *btree_create_node(int leaf) {

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

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

    return node;
}
// 删除节点
void btree_destroy_node(btree_node *node) {
    free(node->childrens);
    free(node->keys);
    free(node);    
}
// 分裂
void btree_split_child(btree *T, btree_node *x, int idx) {

    btree_node *y = x->childrens[idx];
    btree_node *z = btree_create_node(y->leaf);

    //// 对z操作
    z->num = SUB_M - 1;

    int i = 0;
    for (i = 0; i < SUB_M-1; i++) {
        z->keys[i] = y->keys[SUB_M + i];
    }
    
    if (y->leaf == 0) { //inner内结点
        for (i = 0; i < SUB_M; i++) {
            z->childrens[i] = y->childrens[SUB_M + i];
        }
    }
    
    ////对y操作
    y->num = SUB_M - 1;
    for (i = x->num; i >= idx + 1; i--) {
        x->childrens[i + 1] = x->childrens[i];
    }
    x->childrens[idx + 1] = z;

    for (i = x->num-1; i >= idx; i--) {
        x->keys[i+1] = x->keys[i];
    }
    x->keys[idx] = y->keys[SUB_M - 1];
    x->num += 1;
    
}

//插入操作
void btree_insert(btree *T, int key) {

    btree_node *r = T->root;
    
    if (r->num == 2 * SUB_M - 1) {
        
        btree_node *node = btree_create_node(0);
        T->root = node;

        node->childrens[0] = r;

        btree_split_child(T, node, 0);       
    } 
}

//合并操作
void btree_merge(btree *T, btree_node *x, int idx) {

    btree_node *left = x->childrens[idx];
    btree_node *right = x->childrens[idx+1];

    int i = 0;

    /////data merge
    left->keys[left->num] = x->keys[idx];
    for (i = 0;i < right->num; i++) {
        left->keys[SUB_M + i] = right->keys[i];
    }
    if (!left->leaf) {
        for (i = 0;i < SUB_M; i++) {
            left->childrens[SUB_M + i] = right->childrens[i];
        }
    }
    left->num += right->num + 1; // 合并键数应该是左节点数量 + 右节点数量 + 1(来自父节点的键)

    //destroy right
    btree_destroy_node(right);

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

    if (x->num == 0) {
        T->root = left;
        btree_destroy_node(x);
    }
}

参考链接:0voice · GitHub

相关推荐
fish_xk8 小时前
数据结构之二叉树中的堆
数据结构
福尔摩斯张9 小时前
Linux进程间通信(IPC)机制深度解析与实践指南
linux·运维·服务器·数据结构·c++·算法
你好~每一天9 小时前
未来3年,最值得拿下的5个AI证书!
数据结构·人工智能·算法·sqlite·hbase·散列表·模拟退火算法
杰克尼9 小时前
3. 分巧克力
java·数据结构·算法
zmzb01039 小时前
C++课后习题训练记录Day39
数据结构·c++·算法
在下赵某人12 小时前
概率数据结构的设计原理与误差分析
数据结构·算法·哈希算法
fashion 道格12 小时前
深入理解数据结构中的图:邻接链表的应用与实现
数据结构·链表
客梦13 小时前
数据结构基本知识
数据结构
fei_sun13 小时前
【总结】【数据结构】树、二叉树、森林转化
数据结构