1. B树基础与实现原理
B树是一种自平衡的多路搜索树,广泛应用于数据库和文件系统中。与二叉搜索树不同,B树的每个节点可以包含多个键和多个子节点指针,这使得B树在存储大量数据时能够保持较低的树高,从而减少磁盘I/O操作次数。
1.1 B树的核心特性
对于一棵M阶B树,必须满足以下性质:
- 每个节点最多拥有M个子树
- 根节点至少拥有2个子树(除非它是叶子节点)
- 除根节点外,每个内部节点至少拥有⌈M/2⌉个子树
- 所有叶子节点位于同一层
- 有K个子树的节点包含K-1个键,且键按升序排列
- 每个节点的键数量满足⌈M/2⌉-1 ≤ n ≤ M-1
以6阶B树为例:
- 每个节点最多6个子树
- 根节点至少2个子树
- 其他内部节点至少3个子树
- 每个节点键数量范围:2 ≤ n ≤ 5
1.2 B树与B+树的区别
B树和B+树都是多路平衡树,但有几个关键区别:
-
数据存储位置:
- B树:所有节点(包括内部节点)都可以存储数据
- B+树:只有叶子节点存储数据,内部节点仅作索引
-
叶子节点连接:
- B树:叶子节点之间没有链接
- B+树:叶子节点通过指针相连形成链表
-
查询效率:
- B树:可能在内部节点找到数据,查询时间不稳定
- B+树:必须查找到叶子节点,查询时间更稳定
-
范围查询:
- B树:范围查询效率较低
- B+树:通过叶子节点链表可以高效支持范围查询
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. B树的实现细节
2.1 数据结构定义
c复制#define SUB_M 3 // M=6, SUB_M=M/2
typedef int KEY_VALUE; // 可修改为其他类型
typedef struct _btree_node {
KEY_VALUE *keys; // 键数组
struct _btree_node **childrens; // 子节点指针数组
int num; // 当前键数量
int leaf; // 是否为叶子节点
} btree_node;
typedef struct _btree {
btree_node *root;
int t; // M阶,t=M/2
} btree;
2.2 节点创建与销毁
创建节点时需要动态分配内存:
c复制btree_node *btree_create_node(int t, int leaf) {
btree_node *node = (btree_node *)calloc(1, sizeof(btree_node));
if (!node) return NULL;
node->keys = (KEY_VALUE *)calloc(1, (2*t-1)*sizeof(KEY_VALUE));
node->childrens = (btree_node **)calloc(1, (2*t)*sizeof(btree_node*));
if (!node->keys || !node->childrens) {
free(node->keys);
free(node->childrens);
free(node);
return NULL;
}
node->leaf = leaf;
node->num = 0;
return node;
}
销毁节点时需要释放所有分配的内存:
c复制void btree_destroy_node(btree_node *node) {
if (!node) return;
free(node->keys);
free(node->childrens);
free(node);
}
3. B树的核心操作
3.1 插入操作
B树的插入遵循"先分裂再插入"的原则。当节点已满(键数=M-1)时需要进行分裂。
3.1.1 分裂过程
c复制void btree_split_child(btree *T, btree_node *x, int idx) {
int t = T->t;
btree_node *y = x->childrens[idx];
btree_node *z = btree_create_node(t, y->leaf);
z->num = t - 1;
for (int i = 0; i < t-1; i++)
z->keys[i] = y->keys[t+i];
if (!y->leaf) {
for (int i = 0; i < t; i++)
z->childrens[i] = y->childrens[t+i];
}
y->num = t-1;
// 移动父节点的子节点指针
for (int i = x->num; i >= idx+1; i--)
x->childrens[i+1] = x->childrens[i];
x->childrens[idx+1] = z;
// 移动父节点的键
for (int i = x->num-1; i >= idx; i--)
x->keys[i+1] = x->keys[i];
x->keys[idx] = y->keys[t-1];
x->num++;
}
`
