B+树是一种自平衡的树数据结构,广泛应用于数据库和文件系统中,用于高效地存储和检索数据。B+树的特点是每个节点可以拥有多个子节点,并且所有值都保存在叶子节点中,而内部节点只作为导航指针。以下是对 B+ 树实现的详细介绍。
B+ 树的基本特征
阶(Order):B+树的阶定义为一个节点最多可以包含多少个子指针(或键值)。通常用
m
表示。性质:
- 每个内部节点最多有
m
个子指针,最少有 ⌈m/2⌉ 个子指针(除根节点外)。 - 所有叶子节点在同一层,并且按顺序相连,以便能高效进行范围查询。
- 内部节点只存储键值,不直接存储数据。
- 只有叶子节点保存实际的数据记录或数据的指针。
- 每个内部节点最多有
高度平衡:由于每次插入或删除操作可能会导致树的不平衡,B+树通过分裂和合并操作保持其高度平衡,使得任何时候从根到叶子的路径长度是相同的。
B+ 树的基本操作
1. 插入操作
- 从根开始,寻找合适的位置来插入新的键值。
- 如果找到的是一个内部节点,则根据当前键与该键值进行比较并选择合适的子树继续向下搜索。
- 当到达叶子节点时,将新键插入。如果该叶子节点已经满了(即已包含 m−1 个键),则需要进行分裂:
- 将该叶子的元素一部分移至新的叶子结点,并将新生成的叶子结点的第一个键上升至父亲结点。
2. 删除操作
- 查找要删除的键,通过遍历从根到对应叶子的路径找到目标。
- 从叶子中删除该项,如果删除后该叶子不足最小度数 (⌈m/2⌉),则需执行以下步骤之一:
- 从相邻兄弟借用一个元素;
- 或者与兄弟进行合并,形成一个更大的祖先;
这样,可能导致祖先也不满足属性,从而需要逐步向上检查并维护 B+ 树特性。
3. 搜索操作
- 搜索过程类似于二叉搜索树,但使用内部节点仅作为导航器,不直接返回结果。在内部节点上查找以确定应访问哪个子树,然后在对应的叶子中查找实际的数据项。
B+ 树示例代码
下面是一个简化版 B+ 树实现的大概轮廓:
#include <iostream>
#include <vector>
const int MAX_KEYS = 4; // 阶数设为4
class Node {
public:
bool isLeaf;
std::vector<int> keys; // 存储关键字
std::vector<Node*> children; // 存储孩子指针
Node(bool leaf) : isLeaf(leaf) {}
};
class BPlusTree {
private:
Node* root;
void insertNonFull(Node* node, int key) {
// 在非满节中插入
if (node->isLeaf) {
// 找到插入位置
auto it = std::lower_bound(node->keys.begin(), node->keys.end(), key);
node->keys.insert(it, key);
} else {
auto it = std::lower_bound(node->keys.begin(), node->keys.end(), key);
int index = it - node->keys.begin();
if (node->children[index]->keys.size() == MAX_KEYS - 1) {
splitChild(node, index);
if (key > node->keys[index])
index++;
}
insertNonFull(node->children[index], key);
}
}
void splitChild(Node* parent, int index) {
// 分割孩子
Node* fullNode = parent->children[index];
Node* newNode = new Node(fullNode->isLeaf);
for (int i = MAX_KEYS / 2; i < fullNode->keys.size(); ++i)
newNode->keys.push_back(fullNode->keys[i]);
fullNode->keys.resize(MAX_KEYS / 2);
if (!fullNode->isLeaf) {
for (int i = MAX_KEYS / 2 + 1; i <= fullNode->children.size(); ++i)
newNode->children.push_back(fullNode->children[i]);
fullNode->children.resize(MAX_KEYS / 2 + 1);
}
parent->children.insert(parent->children.begin() + index + 1, newNode);
parent->keys.insert(parent->keys.begin() + index, fullNode->keys[MAX_KEYS / 2]);
}
public:
BPlusTree() : root(nullptr) {}
void insert(int key) {
if (!root) {
root = new Node(true);
root->keys.push_back(key); // 若树为空,则创建根结点
} else if (root && root -> keys.size() == MAX_KEYS - 1) {
Node *newRoot= new Node(false);
newRoot -> children.push_back(root);
splitChild(newRoot,0);
int idx=0;
if(newRoot -> keys[0] <key) idx++;
insertNonFull(newRoot -> children[idx],key);
root=newRoot;
} else {
insertNonFull(root,key);
}
}
void traverse() { /* 中序遍历方法 */ }
};
// 示例代码使用
int main() {
BPlusTree bpt;
bpt.insert(10);
bpt.insert(20);
bpt.insert(5);
return 0;
}
总结
B+树提供了快速查找、插入和删除等操作,并且由于其结构特性,在磁盘存取方面表现优越,非常适合用于数据库索引。通过合理管理分支和填充程度,它能够保证良好的性能,是一种非常实用的数据结构。
内容由零声教学AI助手提供,问题来源于学员提问