B树是一种自平衡的多路查找树,广泛应用于数据库管理系统和文件系统中,用于高效地存储和检索大量数据。它是一种特殊的多叉树结构,具有许多独特的性质和优势。
B树是一种平衡的多路查找树,它满足以下性质:
相关代码:
#include
#include
#include
using namespace std;
// B树节点类
template
class BTreeNode {
public:
bool isLeaf; // 是否为叶子节点
vector keys; // 关键字数组
vector children; // 子节点指针数组
int keyCount; // 当前关键字数量
BTreeNode(bool leaf = true) : isLeaf(leaf), keyCount(0) {
keys.resize(M - 1); // B树的阶为M,每个节点最多M-1个关键字
children.resize(M); // 最多M个子节点
}
~BTreeNode() {
for (auto child : children) {
if (child) delete child;
}
}
// 在节点中查找key的位置或应插入的子节点索引
int findKey(const T& key) {
int idx = 0;
while (idx < keyCount && keys[idx] < key) {
++idx;
}
return idx;
}
};
// B树类
template
class BTree {
private:
BTreeNode* root;
// 分裂子节点
void splitChild(BTreeNode* parent, int childIdx) {
BTreeNode* child = parent->children[childIdx];
BTreeNode* newNode = new BTreeNode(child->isLeaf);
// 新节点获取后半部分的关键字
for (int i = 0; i < M/2 - 1; ++i) {
newNode->keys[i] = child->keys[i + M/2];
}
newNode->keyCount = M/2 - 1;
// 如果不是叶子节点,还要复制子节点指针
if (!child->isLeaf) {
for (int i = 0; i < M/2; ++i) {
newNode->children[i] = child->children[i + M/2];
child->children[i + M/2] = nullptr;
}
}
child->keyCount = M/2 - 1;
// 将父节点的关键字和子节点指针后移
for (int i = parent->keyCount; i > childIdx; --i) {
parent->keys[i] = parent->keys[i - 1];
parent->children[i + 1] = parent->children[i];
}
// 将中间关键字提升到父节点
parent->keys[childIdx] = child->keys[M/2 - 1];
parent->children[childIdx + 1] = newNode;
parent->keyCount++;
}
// 插入非满节点
void insertNonFull(BTreeNode* node, const T& key) {
int i = node->keyCount - 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->keyCount++;
} else {
// 找到合适的子节点
while (i >= 0 && key < node->keys[i]) {
i--;
}
i++;
// 检查子节点是否需要分裂
if (node->children[i]->keyCount == M - 1) {
splitChild(node, i);
if (key > node->keys[i]) {
i++;
}
}
insertNonFull(node->children[i], key);
}
}
// 合并子节点
void merge(BTreeNode* parent, int idx) {
BTreeNode* child = parent->children[idx];
BTreeNode* sibling = parent->children[idx + 1];
// 将父节点的关键字下移到子节点
child->keys[M/2 - 1] = parent->keys[idx];
// 复制兄弟节点的关键字和子节点
for (int i = 0; i < sibling->keyCount; ++i) {
child->keys[i + M/2] = sibling->keys[i];
}
if (!child->isLeaf) {
for (int i = 0; i <= sibling->keyCount; ++i) {
child->children[i + M/2] = sibling->children[i];
sibling->children[i] = nullptr;
}
}
// 调整父节点的关键字和子节点指针
for (int i = idx + 1; i < parent->keyCount; ++i) {
parent->keys[i - 1] = parent->keys[i];
parent->children[i] = parent->children[i + 1];
}
child->keyCount += sibling->keyCount + 1;
parent->keyCount--;
delete sibling;
}
// 从子树中删除关键字
void removeFromSubtree(BTreeNode* node, const T& key) {
int idx = node->findKey(key);
if (idx < node->keyCount && node->keys[idx] == key) {
// 关键字在当前节点中
if (node->isLeaf) {
removeFromLeaf(node, idx);
} else {
removeFromNonLeaf(node, idx);
}
} else {
// 关键字不在当前节点中
if (node->isLeaf) {
cout << "Key " << key << " not found in the tree\n";
return;
}
bool flag = (idx == node->keyCount);
// 如果子节点关键字不足,先填充
if (node->children[idx]->keyCount < M/2) {
fill(node, idx);
}
// 如果最后一个子节点被合并,它现在会合并到前一个子节点
if (flag && idx > node->keyCount) {
removeFromSubtree(node->children[idx - 1], key);
} else {
removeFromSubtree(node->children[idx], key);
}
}
}
// 从叶子节点删除关键字
void removeFromLeaf(BTreeNode* node, int idx) {
for (int i = idx + 1; i < node->keyCount; ++i) {
node->keys[i - 1] = node->keys[i];
}
node->keyCount--;
}
// 从非叶子节点删除关键字
void removeFromNonLeaf(BTreeNode* node, int idx) {
T key = node->keys[idx];
// 如果前驱子节点有足够的关键字
if (node->children[idx]->keyCount >= M/2) {
T pred = getPredecessor(node, idx);
node->keys[idx] = pred;
removeFromSubtree(node->children[idx], pred);
}
// 如果后继子节点有足够的关键字
else if (node->children[idx + 1]->keyCount >= M/2) {
T succ = getSuccessor(node, idx);
node->keys[idx] = succ;
removeFromSubtree(node->children[idx + 1], succ);
}
// 如果两个子节点关键字都不足,则合并
else {
merge(node, idx);
removeFromSubtree(node->children[idx], key);
}
}
// 获取前驱关键字
T getPredecessor(BTreeNode* node, int idx) {
BTreeNode* curr = node->children[idx];
while (!curr->isLeaf) {
curr = curr->children[curr->keyCount];
}
return curr->keys[curr->keyCount - 1];
}
// 获取后继关键字
T getSuccessor(BTreeNode* node, int idx) {
BTreeNode* curr = node->children[idx + 1];
while (!curr->isLeaf) {
curr = curr->children[0];
}
return curr->keys[0];
}
// 填充不足的子节点
void fill(BTreeNode* node, int idx) {
// 从前一个子节点借一个关键字
if (idx != 0 && node->children[idx - 1]->keyCount >= M/2) {
borrowFromPrev(node, idx);
}
// 从后一个子节点借一个关键字
else if (idx != node->keyCount && node->children[idx + 1]->keyCount >= M/2) {
borrowFromNext(node, idx);
}
// 合并子节点
else {
if (idx != node->keyCount) {
merge(node, idx);
} else {
merge(node, idx - 1);
}
}
}
// 从前一个子节点借关键字
void borrowFromPrev(BTreeNode* node, int idx) {
BTreeNode* child = node->children[idx];
BTreeNode* sibling = node->children[idx - 1];
// 将父节点的关键字下移到子节点
for (int i = child->keyCount - 1; i >= 0; --i) {
child->keys[i + 1] = child->keys[i];
}
if (!child->isLeaf) {
for (int i = child->keyCount; i >= 0; --i) {
child->children[i + 1] = child->children[i];
}
}
child->keys[0] = node->keys[idx - 1];
if (!child->isLeaf) {
child->children[0] = sibling->children[sibling->keyCount];
sibling->children[sibling->keyCount] = nullptr;
}
node->keys[idx - 1] = sibling->keys[sibling->keyCount - 1];
child->keyCount++;
sibling->keyCount--;
}
// 从后一个子节点借关键字
void borrowFromNext(BTreeNode* node, int idx) {
BTreeNode* child = node->children[idx];
BTreeNode* sibling = node->children[idx + 1];
child->keys[child->keyCount] = node->keys[idx];
if (!child->isLeaf) {
child->children[child->keyCount + 1] = sibling->children[0];
sibling->children[0] = nullptr;
}
node->keys[idx] = sibling->keys[0];
for (int i = 1; i < sibling->keyCount; ++i) {
sibling->keys[i - 1] = sibling->keys[i];
}
if (!sibling->isLeaf) {
for (int i = 1; i <= sibling->keyCount; ++i) {
sibling->children[i - 1] = sibling->children[i];
sibling->children[i] = nullptr;
}
}
child->keyCount++;
sibling->keyCount--;
}
public:
BTree() : root(nullptr) {}
~BTree() {
if (root) delete root;
}
// 查找关键字
bool search(const T& key) {
if (!root) return false;
BTreeNode* curr = root;
while (curr) {
int i = curr->findKey(key);
if (i < curr->keyCount && curr->keys[i] == key) {
return true;
}
if (curr->isLeaf) {
return false;
}
curr = curr->children[i];
}
return false;
}
// 插入关键字
void insert(const T& key) {
if (!root) {
root = new BTreeNode(true);
root->keys[0] = key;
root->keyCount = 1;
return;
}
// 如果根节点已满,需要分裂
if (root->keyCount == M - 1) {
BTreeNode* newRoot = new BTreeNode(false);
newRoot->children[0] = root;
splitChild(newRoot, 0);
// 决定将新关键字插入哪个子节点
int i = 0;
if (newRoot->keys[0] < key) {
i++;
}
insertNonFull(newRoot->children[i], key);
root = newRoot;
} else {
insertNonFull(root, key);
}
}
// 删除关键字
void remove(const T& key) {
if (!root) {
cout << "Tree is empty\n";
return;
}
removeFromSubtree(root, key);
// 如果根节点没有关键字了,使其第一个子节点成为新的根节点
if (root->keyCount == 0) {
BTreeNode* temp = root;
if (root->isLeaf) {
root = nullptr;
} else {
root = root->children[0];
temp->children[0] = nullptr; // 防止析构时被删除
}
delete temp;
}
}
// 打印B树
void print() {
if (root) {
printNode(root, 0);
}
}
private:
void printNode(BTreeNode* node, int level) {
cout << "Level " << level << ": ";
for (int i = 0; i < node->keyCount; ++i) {
cout << node->keys[i] << " ";
}
cout << endl;
if (!node->isLeaf) {
for (int i = 0; i <= node->keyCount; ++i) {
if (node->children[i]) {
printNode(node->children[i], level + 1);
}
}
}
}
};
int main() {
BTree tree; // 创建一个4阶B树
// 插入测试
tree.insert(10);
tree.insert(20);
tree.insert(5);
tree.insert(6);
tree.insert(12);
tree.insert(30);
tree.insert(7);
tree.insert(17);
cout << "B树结构:" << endl;
tree.print();
// 查找测试
cout << "\n查找测试:" << endl;
cout << "6 " << (tree.search(6) ? "存在" : "不存在") << endl;
cout << "15 " << (tree.search(15) ? "存在" : "不存在") << endl;
// 删除测试
cout << "\n删除6后:" << endl;
tree.remove(6);
tree.print();
cout << "\n删除13(不存在)后:" << endl;
tree.remove(13);
tree.print();
cout << "\n删除30后:" << endl;
tree.remove(30);
tree.print();
return 0;
}
代码运行结果:
isLeaf
:标记是否为叶子节点keys
:存储关键字的数组children
:存储子节点指针的数组keyCount
:当前节点中关键字的数量insert
:插入新关键字remove
:删除关键字search
:查找关键字print
:打印B树结构splitChild
:分裂子节点merge
:合并子节点borrowFromPrev/Next
:从相邻节点借关键字insertNonFull
:向非满节点插入关键字removeFromSubtree
:从子树中删除关键字说明:以上代码有DeepSeek生成。