b+树详解,代码实例
时间: 2024-06-18 18:05:33 浏览: 196
B+树是一种常用的数据结构,它可以在磁盘上高效地存储和检索数据。B+树是一种平衡树,它的每个非叶子节点可以有多个子节点,而且每个子节点都存储着一定范围内的关键字。
B+树的特点如下:
1. 所有数据都存储在叶子节点中,非叶子节点只存储索引信息。
2. 叶子节点之间是有序的,并且形成了一个链表,可以支持范围查询和顺序访问。
3. 非叶子节点中的索引信息是有序的,并且可以通过索引信息快速定位到对应的叶子节点。
4. B+树的每个节点都至少包含 t 个关键字,其中 t 是一个常数(通常取值为2-4)。
B+树的插入和删除操作比较复杂,需要进行分裂和合并等操作来保证树的平衡性。下面是B+树的插入和删除过程:
插入操作:
1. 如果树为空,则新建一个叶子节点并插入关键字。
2. 如果插入的位置在叶子节点中,则直接插入关键字,并保持叶子节点有序。
3. 如果插入的位置在非叶子节点中,则递归地将关键字插入到对应的子节点中。
4. 如果插入后导致节点关键字数目超过了 t,则进行分裂操作,并将分裂后的两个节点插入到父节点中。
删除操作:
1. 如果要删除的关键字在叶子节点中,则直接删除,并保持叶子节点有序。
2. 如果要删除的关键字在非叶子节点中:
- 如果对应子节点中的关键字数量大于 t,则找到前驱或后继关键字替换,并递归删除替换后的关键字。
- 如果对应子节点中的关键字数量等于 t,则进行合并操作,并递归删除合并后的关键字。
3. 如果删除导致某个节点的关键字数量小于 t/2,则进行合并操作,并将合并后的节点插入到父节点中。
下面是一个简单的B+树实现代码(仅供参考):
```c++
struct Node {
bool isLeaf;
int numKeys;
int keys[MAX_KEYS];
Node* children[MAX_CHILDREN];
Node* next;
};
Node* createNode(bool isLeaf) {
Node* node = new Node;
node->isLeaf = isLeaf;
node->numKeys = 0;
node->next = NULL;
for (int i = 0; i < MAX_KEYS; i++) {
node->keys[i] = 0;
}
for (int i = 0; i < MAX_CHILDREN; i++) {
node->children[i] = NULL;
}
return node;
}
Node* search(Node* node, int key) {
if (node == NULL) {
return NULL;
}
int i = 0;
while (i < node->numKeys && key > node->keys[i]) {
i++;
}
if (node->isLeaf && i < node->numKeys && key == node->keys[i]) {
return node;
}
return search(node->children[i], key);
}
void split(Node* parent, int pos, Node* child) {
Node* newChild = createNode(child->isLeaf);
newChild->numKeys = MAX_KEYS / 2;
for (int i = 0; i < newChild->numKeys; i++) {
newChild->keys[i] = child->keys[i + MAX_KEYS / 2];
}
if (!child->isLeaf) {
for (int i = 0; i < MAX_CHILDREN / 2; i++) {
newChild->children[i] = child->children[i + MAX_CHILDREN / 2];
}
}
child->numKeys = MAX_KEYS / 2;
for (int i = parent->numKeys; i > pos; i--) {
parent->children[i + 1] = parent->children[i];
parent->keys[i] = parent->keys[i - 1];
}
parent->children[pos + 1] = newChild;
parent->keys[pos] = newChild->keys;
parent->numKeys++;
}
void insert(Node*& root, int key) {
if (root == NULL) {
root = createNode(true);
root->keys = key;
root->numKeys++;
return;
}
Node* node = root;
Node* parent = NULL;
int pos = -1;
while (!node->isLeaf) {
parent = node;
pos = -1;
for (int i = 0; i < node->numKeys; i++) {
if (key >= node->keys[i]) {
pos = i;
} else {
break;
}
}
if (pos == -1) {
node = node->children;
} else {
node = node->children[pos + 1];
}
}
pos = -1;
for (int i = 0; i < node->numKeys; i++) {
if (key == node->keys[i]) {
return;
} else if (key > node->keys[i]) {
pos = i;
} else {
break;
}
}
for (int i = node->numKeys; i > pos + 1; i--) {
node->keys[i] = node->keys[i - 1];
}
node->keys[pos + 1] = key;
node->numKeys++;
if (node->numKeys == MAX_KEYS) {
if (parent == NULL) {
parent = createNode(false);
root = parent;
parent->children = node;
}
split(parent, pos, node);
}
}
void merge(Node* parent, int pos, Node* left, Node* right) {
left->keys[left->numKeys++] = parent->keys[pos];
for (int i = 0; i < right->numKeys; i++) {
left->keys[left->numKeys++] = right->keys[i];
}
if (!left->isLeaf) {
for (int i = 0; i < right->numKeys; i++) {
left->children[left->numKeys + i] = right->children[i];
}
}
left->next = right->next;
delete right;
for (int i = pos; i < parent->numKeys - 1; i++) {
parent->children[i + 1] = parent->children[i + 2];
parent->keys[i] = parent->keys[i + 1];
}
parent->numKeys--;
}
void remove(Node*& root, int key) {
if (root == NULL) {
return;
}
Node* node = root;
Node* parent = NULL;
int pos = -1;
while (!node->isLeaf) {
parent = node;
pos = -1;
for (int i = 0; i < node->numKeys; i++) {
if (key >= node->keys[i]) {
pos = i;
} else {
break;
}
}
if (pos == -1) {
node = node->children;
} else {
node = node->children[pos + 1];
}
}
pos = -1;
for (int i = 0; i < node->numKeys; i++) {
if (key == node->keys[i]) {
pos = i;
break;
}
}
if (pos == -1) {
return;
}
for (int i = pos; i < node->numKeys - 1; i++) {
node->keys[i] = node->keys[i + 1];
}
node->numKeys--;
if (node == root) { // root is a leaf
return;
}
while (node != root && node != NULL && node->numKeys < MAX_KEYS / 2) {
int leftPos = -1, rightPos = -1;
for (int i = 0; i <= parent->numKeys; i++) {
if (parent->children[i] == node) {
leftPos = i - 1;
rightPos = i + 1;
break;
}
}
Node *leftChild, *rightChild;
bool flagLeftOverFlow, flagRightOverFlow;
if (leftPos >= 0) { // has left sibling
leftChild = parent->children[leftPos];
flagLeftOverFlow =
leftChild != NULL && leftChild != NULL &&
leftChild->numKeys > MAX_KEYS / 2; // whether left sibling can
// afford one key
if (!flagLeftOverFlow && leftPos >= 0 && rightPos <= parent->numKeys) { // merge with left sibling
merge(parent, leftPos, leftChild, node);
node =
leftChild; // after merge, the current position is the new merged child
continue;
}
} else { // no left sibling
flagLeftOverFlow =
false; // this flag is set to false to ensure that it will not trigger merge operation with left sibling later
leftChild =
NULL; // leftChild is set to NULL to avoid using it later
}
if (rightPos <= parent->numKeys) { // has right sibling
rightChild =
parent
->children[rightPos]; // after merge operation with left sibling, right sibling could have been moved to the current position
// therefore, rightPos should be calculated again
flagRightOverFlow =
rightChild != NULL && rightChild != NULL &&
rightChild
->numKeys > MAX_KEYS / 2; // whether right sibling can afford one key
if (!flagRightOverFlow && leftPos >= 0 && rightPos <= parent->numKeys) { // merge with right sibling
merge(parent, leftPos, node, rightChild);
node =
leftChild; // after merge, the current position is the new merged child
continue;
}
} else { // no right sibling
flagRightOverFlow =
false; // this flag is set to false to ensure that it will not trigger merge operation with right sibling later
rightChild =
NULL; // rightChild is set to NULL to avoid using it later
}
if (flagLeftOverFlow || flagRightOverFlow) { // borrow from sibling and update parent's keys
if (flagLeftOverFlow &&
(!flagRightOverFlow ||
leftChild
->numKeys >
rightChild
->numKeys)) { // prefer borrowing from left sibling
for (int i =
node
->numKeys -
1; // move current keys to make room for borrowed key from sibling
i >= 0; --i) {
node
->keys[i +
1] =
node
->keys[i];
}
if (!node
->isLeaf) { // move children accordingly for non-leaf nodes
for (int i =
node->
numKeys -
1; i >=
0; --i) {
node
->children[i +
2] =
node->
children[i +
1];
}
node
->children =
leftChild->
children[leftChild->
numKeys +
1];
++node->
numKeys;
leftChild->
children[leftChild->
numKeys +
1] =
NULL;
--leftChild->
numKeys;
parent->
keys[leftPos] =
leftChild->
keys[leftChild->
numKeys -
1];
leftChild->
keys[leftChild->
numKeys -
1] =
0;
continue;
} else { // leaf nodes need to update next pointer as well
++node->
numKeys;
int newKeyIndex =
--leftChild->
numKeys;
parent->
keys[leftPos] =
leftChild->
keys[newKeyIndex];
++node->
numKeys;
node
->next =
leftChild->
next;
leftChild->
next =
node;
continue;
}
} else { // borrow from right sibling or its children
if (!node->
isLeaf) { // move children accordingly for non-leaf nodes
++node->
numKeys;
int newKeyIndex =
++node->
numKeys;
if (newKeyIndex >= MAX_KEYS)
newKeyIndex--;
node
->children[newKeyIndex +
1] =
rightChild->
children;
parent->
keys[rightPos -
1] =
rightChild->
keys;
++rightChild->
numKeys;
for (int k =
0; k <
rightChild->
numKeys -
1; ++k)
rightChild->
keys[k] =
rightChild->
keys[k +
1];
for (int k =
0; k <
rightChild->
numKeys +
1; ++k)
rightChild->
children[k] =
rightChild->
children[k +
1];
--rightChild->
numKeys;
continue;
} else { // leaf nodes need to update next pointer as well
++node->
numKeys;
int newKeyIndex =
++node->
numKeys;
if (newKeyIndex >= MAX_KEYS)
newKeyIndex--;
node
->keys[newKeyIndex -
1] =
rightChild->
keys;
++rightChild->
numKeys;
for (int k =
0; k <
rightChild->
numKeys -
1; ++k)
rightChild->
keys[k] =
rightChild->
keys[k +
1];
--rightChild->
numKeys;
parent->
keys[rightPos -
1] =
rightChild->
keys;
continue;
}
}
}
break;
}
}
```
阅读全文