B树B+树
#include <stdio.h>
#include <stdlib.h>
// 定义 B 树的最小度数(每个节点至少有 t 个孩子,最多有 2t - 1 个键值)
#define T 3
// B 树节点结构
typedef struct BTreeNode {
int *keys; // 存储键值的数组
int t; // 最小度数
struct BTreeNode **C; // 子节点指针数组
int n; // 当前节点的键值个数
int leaf; // 是否是叶子节点
} BTreeNode;
// 创建一个新的 B 树节点
BTreeNode *createNode(int t, int leaf) {
BTreeNode *node = (BTreeNode *)malloc(sizeof(BTreeNode));
if (!node) {
fprintf(stderr, "Memory allocation failed for BTreeNode.\n");
exit(EXIT_FAILURE);
}
node->t = t;
node->leaf = leaf;
node->keys = (int *)malloc((2 * t - 1) * sizeof(int));
node->C = (BTreeNode **)malloc(2 * t * sizeof(BTreeNode *));
node->n = 0;
if (!node->keys || !node->C) {
fprintf(stderr, "Memory allocation failed for keys or children.\n");
exit(EXIT_FAILURE);
}
return node;
}
// 遍历 B 树(中序遍历)
void traverse(BTreeNode *root) {
if (root == NULL) return;
// 遍历每个键值前的子树
for (int i = 0; i < root->n; i++) {
if (!root->leaf) {
traverse(root->C[i]);
}
printf("%d ", root->keys[i]); // 打印键值
}
// 遍历最后一个子树
if (!root->leaf) {
traverse(root->C[root->n]);
}
}
// 搜索键值
BTreeNode *search(BTreeNode *root, int k) {
int i = 0;
// 在当前节点中查找键值的位置
while (i < root->n && k > root->keys[i]) {
i++;
}
// 如果找到键值,返回当前节点
if (i < root->n && root->keys[i] == k) {
return root;
}
// 如果是叶子节点,直接返回 NULL
if (root->leaf) {
return NULL;
}
// 在对应的子节点递归查找
return search(root->C[i], k);
}
// 分裂子节点 y
void splitChild(BTreeNode *parent, int i, BTreeNode *y) {
int t = y->t;
// 创建新节点 z,存储 y 的后半部分键值和子节点
BTreeNode *z = createNode(t, y->leaf);
z->n = t - 1;
// 将 y 的后半部分键值复制到 z
for (int j = 0; j < t - 1; j++) {
z->keys[j] = y->keys[j + t];
}
// 如果 y 不是叶子节点,将 y 的后半部分子节点指针复制到 z
if (!y->leaf) {
for (int j = 0; j < t; j++) {
z->C[j] = y->C[j + t];
}
}
y->n = t - 1; // 更新 y 的键值个数
// 将 z 插入到 parent 的子节点列表中
for (int j = parent->n; j >= i + 1; j--) {
parent->C[j + 1] = parent->C[j];
}
parent->C[i + 1] = z;
// 将 y 的中间键提升到 parent
for (int j = parent->n - 1; j >= i; j--) {
parent->keys[j + 1] = parent->keys[j];
}
parent->keys[i] = y->keys[t - 1];
parent->n++;
}
// 插入到非满节点中
void insertNonFull(BTreeNode *node, int k) {
int i = node->n - 1;
// 如果是叶子节点
if (node->leaf) {
// 找到插入位置并移动键值
while (i >= 0 && k < node->keys[i]) {
node->keys[i + 1] = node->keys[i];
i--;
}
node->keys[i + 1] = k; // 插入键值
node->n++;
} else {
// 找到子节点的位置
while (i >= 0 && k < node->keys[i]) {
i--;
}
i++;
// 如果子节点已满,先分裂子节点
if (node->C[i]->n == 2 * node->t - 1) {
splitChild(node, i, node->C[i]);
// 判断 k 应插入到哪个子节点
if (k > node->keys[i]) {
i++;
}
}
insertNonFull(node->C[i], k);
}
}
// 插入键值到 B 树
void insert(BTreeNode **rootRef, int k, int t) {
BTreeNode *root = *rootRef;
// 如果根节点已满
if (root->n == 2 * t - 1) {
// 创建新根节点
BTreeNode *newRoot = createNode(t, 0);
newRoot->C[0] = root;
// 分裂旧根节点
splitChild(newRoot, 0, root);
// 插入到合适的子节点
int i = 0;
if (k > newRoot->keys[0]) {
i++;
}
insertNonFull(newRoot->C[i], k);
*rootRef = newRoot; // 更新根节点
} else {
insertNonFull(root, k);
}
}
// 释放 B 树节点
void freeBTree(BTreeNode *node) {
if (node == NULL) return;
if (!node->leaf) {
for (int i = 0; i <= node->n; i++) {
freeBTree(node->C[i]);
}
}
free(node->keys);
free(node->C);
free(node);
}
// 主函数
int main() {
// 创建空 B 树
BTreeNode *root = createNode(T, 1);
// 插入一些数据
insert(&root, 10, T);
insert(&root, 20, T);
insert(&root, 5, T);
insert(&root, 6, T);
insert(&root, 12, T);
insert(&root, 30, T);
insert(&root, 7, T);
insert(&root, 17, T);
// 遍历 B 树
printf("Traversal of the B-Tree:\n");
traverse(root);
printf("\n");
// 搜索键值
int key = 6;
printf("Searching for key %d in the B-Tree...\n", key);
BTreeNode *result = search(root, key);
if (result != NULL) {
printf("Key %d found in the B-Tree.\n", key);
} else {
printf("Key %d not found in the B-Tree.\n", key);
}
// 释放 B 树
freeBTree(root);
return 0;
}
C语言b树
数据结构:
- 每个节点存储一个
keys
数组和一个C
子节点指针数组。n
表示当前键值数,leaf
表示是否为叶子节点。主要功能:
- 创建节点:
createNode
动态分配节点。- 插入:插入时处理节点分裂,保证 B 树平衡。
- 查找:递归在树中查找键值。
- 遍历:按中序遍历输出键值。
- 释放内存:
freeBTree
确保无内存泄漏。增强健壮性:
- 内存分配检测:每次动态分配都检查是否成功。
- 边界条件处理:空树或单节点插入、满节点分裂等情况均已处理。
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
// 定义 B 树的最小度数
const int T = 3; // 每个节点最多 2*T-1 个键,最少 T-1 个键
// B 树节点类
class BTreeNode {
public:
vector<int> keys; // 键值数组
vector<shared_ptr<BTreeNode>> children; // 子节点指针数组
int t; // 最小度数
bool leaf; // 是否为叶子节点
// 构造函数
BTreeNode(int t, bool leaf) : t(t), leaf(leaf) {}
// 遍历 B 树(中序遍历)
void traverse() {
int i;
// 遍历每个键值前的子树
for (i = 0; i < keys.size(); i++) {
if (!leaf) {
children[i]->traverse();
}
cout << keys[i] << " ";
}
// 遍历最后一个子树
if (!leaf) {
children[i]->traverse();
}
}
// 搜索键值
shared_ptr<BTreeNode> search(int k) {
int i = 0;
// 找到第一个大于或等于 k 的键
while (i < keys.size() && k > keys[i]) {
i++;
}
// 如果键值等于 k,返回当前节点
if (i < keys.size() && keys[i] == k) {
return shared_from_this();
}
// 如果是叶子节点,则键值不存在
if (leaf) {
return nullptr;
}
// 在对应的子节点递归查找
return children[i]->search(k);
}
// 分裂子节点
void splitChild(int i, shared_ptr<BTreeNode> y) {
// 创建一个新节点 z,存储 y 的后半部分键值和子节点
auto z = make_shared<BTreeNode>(y->t, y->leaf);
z->keys.resize(t - 1);
for (int j = 0; j < t - 1; j++) {
z->keys[j] = y->keys[j + t];
}
// 如果 y 不是叶子节点,将 y 的后半部分子节点移动到 z
if (!y->leaf) {
z->children.resize(t);
for (int j = 0; j < t; j++) {
z->children[j] = y->children[j + t];
}
}
// 调整 y 的键值数
y->keys.resize(t - 1);
// 在父节点中插入新的子节点 z
children.insert(children.begin() + i + 1, z);
// 将 y 的中间键提升到父节点
keys.insert(keys.begin() + i, y->keys[t - 1]);
}
// 插入到非满节点中
void insertNonFull(int k) {
int i = keys.size() - 1;
if (leaf) {
// 如果是叶子节点,找到插入位置并插入
while (i >= 0 && k < keys[i]) {
i--;
}
keys.insert(keys.begin() + i + 1, k);
} else {
// 找到子节点位置
while (i >= 0 && k < keys[i]) {
i--;
}
i++;
// 如果子节点已满,先分裂子节点
if (children[i]->keys.size() == 2 * t - 1) {
splitChild(i, children[i]);
// 判断插入位置
if (k > keys[i]) {
i++;
}
}
children[i]->insertNonFull(k);
}
}
};
// B 树类
class BTree {
private:
shared_ptr<BTreeNode> root; // 根节点
int t; // 最小度数
public:
// 构造函数
BTree(int t) : t(t), root(nullptr) {}
// 遍历 B 树
void traverse() {
if (root != nullptr) {
root->traverse();
}
}
// 搜索键值
shared_ptr<BTreeNode> search(int k) {
if (root == nullptr) {
return nullptr;
} else {
return root->search(k);
}
}
// 插入键值
void insert(int k) {
if (root == nullptr) {
// 如果根节点为空,创建一个新节点
root = make_shared<BTreeNode>(t, true);
root->keys.push_back(k);
} else {
// 如果根节点已满
if (root->keys.size() == 2 * t - 1) {
// 创建一个新根节点
auto newRoot = make_shared<BTreeNode>(t, false);
// 将旧根节点作为新根的子节点
newRoot->children.push_back(root);
// 分裂旧根节点
newRoot->splitChild(0, root);
// 插入到合适的子节点
if (k > newRoot->keys[0]) {
newRoot->children[1]->insertNonFull(k);
} else {
newRoot->children[0]->insertNonFull(k);
}
root = newRoot; // 更新根节点
} else {
root->insertNonFull(k);
}
}
}
};
// 主函数
int main() {
// 创建 B 树
BTree tree(T);
// 插入一些数据
tree.insert(10);
tree.insert(20);
tree.insert(5);
tree.insert(6);
tree.insert(12);
tree.insert(30);
tree.insert(7);
tree.insert(17);
// 遍历 B 树
cout << "Traversal of the B-Tree:\n";
tree.traverse();
cout << endl;
// 搜索键值
int key = 6;
cout << "Searching for key " << key << " in the B-Tree..." << endl;
auto result = tree.search(key);
if (result != nullptr) {
cout << "Key " << key << " found in the B-Tree.\n";
} else {
cout << "Key " << key << " not found in the B-Tree.\n";
}
return 0;
}
CPP版本b树
数据结构设计:
BTreeNode
类表示 B 树的节点,包含键值数组 (keys
) 和子节点指针数组 (children
)。BTree
类表示 B 树,封装了树的根节点和相关操作。核心操作:
- 创建节点:通过构造函数动态创建新节点。
- 插入:
- 如果根节点已满,会创建一个新根节点并分裂旧根。
- 插入操作通过递归调整子节点,确保 B 树的平衡性。
- 查找:在节点中二分查找键值并递归到子节点。
- 遍历:中序遍历所有键值。
增强健壮性:
- 使用
vector
自动管理数组内存,避免手动分配和释放。- 使用
shared_ptr
管理节点的生命周期,防止内存泄漏。- 异常检测和边界条件处理(如空树或满节点)。
输入输出:
- 插入一组数据构建 B 树。
- 遍历 B 树,输出所有键值。
- 搜索指定键值,输出是否存在。
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// 定义 B+树的最小度数 T
#define T 3
// 定义 B+树节点结构
typedef struct BPTreeNode {
int *keys; // 键值数组
struct BPTreeNode **children; // 子节点指针数组
int n; // 当前节点的键值数量
bool isLeaf; // 是否为叶子节点
struct BPTreeNode *next; // 用于叶子节点的链表指针
} BPTreeNode;
// 创建一个新节点
BPTreeNode *createNode(bool isLeaf) {
BPTreeNode *node = (BPTreeNode *)malloc(sizeof(BPTreeNode));
if (!node) {
fprintf(stderr, "Memory allocation failed for BPTreeNode.\n");
exit(EXIT_FAILURE);
}
node->keys = (int *)malloc((2 * T - 1) * sizeof(int));
node->children = (BPTreeNode **)malloc(2 * T * sizeof(BPTreeNode *));
node->n = 0;
node->isLeaf = isLeaf;
node->next = NULL;
if (!node->keys || !node->children) {
fprintf(stderr, "Memory allocation failed for keys or children.\n");
exit(EXIT_FAILURE);
}
return node;
}
// 遍历 B+树的叶子节点(范围查询)
void traverse(BPTreeNode *root) {
if (!root) return;
// 找到最左叶子节点
while (!root->isLeaf) {
root = root->children[0];
}
// 遍历所有叶子节点
while (root) {
for (int i = 0; i < root->n; i++) {
printf("%d ", root->keys[i]);
}
root = root->next;
}
printf("\n");
}
// 搜索键值
BPTreeNode *search(BPTreeNode *root, int key) {
if (!root) return NULL;
int i = 0;
// 找到键值范围
while (i < root->n && key > root->keys[i]) {
i++;
}
if (root->isLeaf) {
// 如果是叶子节点,检查是否找到
if (i < root->n && root->keys[i] == key) {
return root;
}
return NULL;
}
// 如果不是叶子节点,递归到子节点
return search(root->children[i], key);
}
// 分裂非叶子节点的子节点
void splitNonLeafChild(BPTreeNode *parent, int idx, BPTreeNode *child) {
// 创建新节点
BPTreeNode *newNode = createNode(child->isLeaf);
newNode->n = T - 1;
// 将后半部分的键值复制到新节点
for (int i = 0; i < T - 1; i++) {
newNode->keys[i] = child->keys[i + T];
}
// 如果不是叶子节点,将子节点指针复制到新节点
if (!child->isLeaf) {
for (int i = 0; i < T; i++) {
newNode->children[i] = child->children[i + T];
}
}
child->n = T - 1;
// 插入新的子节点到父节点
for (int i = parent->n; i >= idx + 1; i--) {
parent->children[i + 1] = parent->children[i];
}
parent->children[idx + 1] = newNode;
// 将中间键值插入到父节点
for (int i = parent->n - 1; i >= idx; i--) {
parent->keys[i + 1] = parent->keys[i];
}
parent->keys[idx] = child->keys[T - 1];
parent->n++;
}
// 分裂叶子节点
void splitLeafChild(BPTreeNode *parent, int idx, BPTreeNode *leaf) {
// 创建一个新叶子节点
BPTreeNode *newLeaf = createNode(true);
newLeaf->n = T - 1;
// 将后半部分的键值复制到新叶子节点
for (int i = 0; i < T - 1; i++) {
newLeaf->keys[i] = leaf->keys[i + T - 1];
}
leaf->n = T - 1;
// 设置叶子节点的链表指针
newLeaf->next = leaf->next;
leaf->next = newLeaf;
// 插入新叶子节点到父节点
for (int i = parent->n; i >= idx + 1; i--) {
parent->children[i + 1] = parent->children[i];
}
parent->children[idx + 1] = newLeaf;
// 将新叶子节点的第一个键值插入到父节点
for (int i = parent->n - 1; i >= idx; i--) {
parent->keys[i + 1] = parent->keys[i];
}
parent->keys[idx] = newLeaf->keys[0];
parent->n++;
}
// 插入到非满节点中
void insertNonFull(BPTreeNode *node, int key) {
int i = node->n - 1;
if (node->isLeaf) {
// 找到插入位置
while (i >= 0 && key < node->keys[i]) {
node->keys[i + 1] = node->keys[i];
i--;
}
node->keys[i + 1] = key;
node->n++;
} else {
// 找到子节点位置
while (i >= 0 && key < node->keys[i]) {
i--;
}
i++;
// 如果子节点已满
if (node->children[i]->n == 2 * T - 1) {
if (node->children[i]->isLeaf) {
splitLeafChild(node, i, node->children[i]);
} else {
splitNonLeafChild(node, i, node->children[i]);
}
// 确定插入位置
if (key > node->keys[i]) {
i++;
}
}
insertNonFull(node->children[i], key);
}
}
// 插入键值到 B+树
void insert(BPTreeNode **rootRef, int key) {
BPTreeNode *root = *rootRef;
if (!root) {
// 如果根节点为空,创建一个新节点
root = createNode(true);
root->keys[0] = key;
root->n = 1;
*rootRef = root;
return;
}
// 如果根节点已满
if (root->n == 2 * T - 1) {
// 创建一个新根节点
BPTreeNode *newRoot = createNode(false);
newRoot->children[0] = root;
// 分裂根节点
if (root->isLeaf) {
splitLeafChild(newRoot, 0, root);
} else {
splitNonLeafChild(newRoot, 0, root);
}
// 插入到新根节点的合适位置
int i = (key > newRoot->keys[0]) ? 1 : 0;
insertNonFull(newRoot->children[i], key);
*rootRef = newRoot;
} else {
insertNonFull(root, key);
}
}
// 释放 B+树节点
void freeBPTree(BPTreeNode *node) {
if (!node) return;
if (!node->isLeaf) {
for (int i = 0; i <= node->n; i++) {
freeBPTree(node->children[i]);
}
}
free(node->keys);
free(node->children);
free(node);
}
// 主函数
int main() {
BPTreeNode *root = NULL;
// 插入数据
insert(&root, 10);
insert(&root, 20);
insert(&root, 5);
insert(&root, 6);
insert(&root, 12);
insert(&root, 30);
insert(&root, 7);
insert(&root, 17);
// 遍历 B+树
printf("Traversal of the B+ Tree:\n");
traverse(root);
// 搜索键值
int key = 6;
printf("Searching for key %d in the B+ Tree...\n", key);
BPTreeNode *result = search(root, key);
if (result) {
printf("Key %d found in the B+ Tree.\n", key);
} else {
printf("Key %d not found in the B+ Tree.\n", key);
}
// 释放 B+树
freeBPTree(root);
return 0;
}
C语言版本b+树
数据结构:
BPTreeNode
表示 B+树的节点,包含keys
、children
和next
(叶子节点链表指针)。- 支持叶子节点链表结构,优化范围查询。
主要功能:
- 插入:自动处理节点分裂,支持叶子和非叶子节点分裂。
- 搜索:递归搜索键值。
- 遍历:从叶子节点链表输出所有键值。
- 内存释放:递归释放所有节点,避免内存泄漏。
健壮性:
- 检查内存分配是否成功,失败时终止程序。
- 处理空树和根节点分裂等边界情况。
测试功能:
- 插入一组数据,输出所有键值。
- 搜索特定键值,输出是否找到。