在内存中,红黑树表现优秀。但在磁盘或 SSD 上的大规模数据结构中,它就不那么合适了:
B 树(Balanced Tree)最早由 Bayer 和 McCreight 于 1972 年提出。
一个 m 阶 B 树满足以下条件:
k
个 key,满足:k = 子节点数 - 1 [17 35]
/ | \
[5 8] [20 22] [40 60]
结构方面 |
B 树 |
B+ 树 |
叶子节点 |
含数据(部分) |
全部数据只在叶子节点中 |
非叶子节点 |
存储 key 和数据指针 |
只存储 key,不存数据 |
有序遍历 |
需要中序递归 |
叶子节点链式连接,遍历更快 |
查询性能 |
较差(中途返回数据) |
一致性强(固定深度,访问叶子) |
[5 8] → [12 17] → [20 22] → [40 60]
这使得范围查找和分页查询变得非常高效(几乎是数据库的必备功能)
在现代数据库系统或操作系统中,磁盘按页管理:
结构项 |
描述 |
页大小 |
通常是 4KB(4096 字节) |
数据对齐 |
每个数据结构要对齐页 |
B+ 树节点设计 |
通常设计为一个节点=一个页 |
场景 |
推荐结构 |
原因 |
操作系统文件索引 |
B+ 树 |
范围查找+分页遍历更强 |
数据库索引结构(如 MySQL) |
B+ 树 |
结构化磁盘页管理 |
小型内存数据结构 |
B 树或红黑树 |
不涉及磁盘,可用 B 树/红黑树 |
以下是一个简化的 B 树实现(m=3):
#include
#include
#include
const int DEGREE = 3; // m 阶 B 树,最多 2*DEGREE - 1 个 key
class BTreeNode {
public:
bool isLeaf;
std::vector keys;
std::vector children;
BTreeNode(bool leaf) : isLeaf(leaf) {}
void traverse() {
for (size_t i = 0; i < keys.size(); ++i) {
if (!isLeaf) children[i]->traverse();
std::cout << keys[i] << " ";
}
if (!isLeaf) children[keys.size()]->traverse();
}
BTreeNode* search(int key) {
int i = 0;
while (i < keys.size() && key > keys[i]) ++i;
if (i < keys.size() && key == keys[i]) return this;
if (isLeaf) return nullptr;
return children[i]->search(key);
}
};
class BTree {
BTreeNode* root;
public:
BTree() : root(nullptr) {}
void traverse() {
if (root != nullptr) root->traverse();
}
BTreeNode* search(int key) {
return (root == nullptr) ? nullptr : root->search(key);
}
// 插入逻辑略复杂,建议下一节手动实现
};
这里只写了查找和遍历,完整的插入+分裂逻辑我们将在下一节详细展开并实现
✅ 多数数据库索引(如 MySQL 的 InnoDB)使用 B+ 树
原因:
重点点 |
内容 |
B 树特点 |
多路查找、节点包含多个 key、结构平衡 |
B+ 树优化 |
所有数据都在叶子节点、非叶子仅做索引、叶子链式连接 |
工程优势 |
高度低、减少磁盘 I/O、结构清晰、页对齐优化 |
应用 |
文件系统索引、数据库索引、范围查找、高效分页遍历 |