#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define M 4 // M阶B树的度为4,也就是每个节点最多可以有4棵子树
// M阶B树的节点结构体
struct node {
int keys[M - 1]; // 关键字数组
struct node* children[M]; // 子节点指针数组
int nKeys; // 关键字的数量
bool leaf; // 是否为叶子节点
};
// 创建新的M阶B树节点
struct node* createNode(bool isLeaf) {
struct node* newNode = (struct node*) malloc(sizeof(struct node));
newNode->nKeys = 0;
newNode->leaf = isLeaf;
for (int i = 0; i < M; i++) {
newNode->children[i] = NULL;
}
return newNode;
}
// 在当前节点上查找给定的键值,并返回该键值在节点中的索引
int findKeyIndex(struct node* currentNode, int key) {
int index = 0;
while (index < currentNode->nKeys && currentNode->keys[index] < key) {
index++;
}
return index;
}
// 分裂指定的子节点
void splitChild(struct node* currentNode, int childIndex, struct node* childNode) {
struct node* newChildNode = createNode(childNode->leaf);
newChildNode->nKeys = M / 2 - 1;
for (int i = 0; i < M / 2 - 1; i++) {
newChildNode->keys[i] = childNode->keys[i + M / 2];
}
if (!childNode->leaf) {
for (int i = 0; i < M / 2; i++) {
newChildNode->children[i] = childNode->children[i + M / 2];
}
}
childNode->nKeys = M / 2 - 1;
for (int i = currentNode->nKeys; i >= childIndex + 1; i--) {
currentNode->children[i + 1] = currentNode->children[i];
}
currentNode->children[childIndex + 1] = newChildNode;
for (int i = currentNode->nKeys - 1; i >= childIndex; i--) {
currentNode->keys[i + 1] = currentNode->keys[i];
}
currentNode->keys[childIndex] = childNode->keys[M / 2 - 1];
currentNode->nKeys++;
}
// 在当前节点上插入一个新的键值对
void insertNonFull(struct node* currentNode, int key) {
int index = currentNode->nKeys - 1;
if (currentNode->leaf) {
// 如果节点是叶子节点,则找到适当的插入位置
while (index >= 0 && currentNode->keys[index] > key) {
currentNode->keys[index + 1] = currentNode->keys[index];
index--;
}
currentNode->keys[index + 1] = key;
currentNode->nKeys++;
} else {
// 如果节点是内部节点,则找到该键应该插入的子节点,并插入到子节点中
while (index >= 0 && currentNode->keys[index] > key) {
index--;
}
if (currentNode->children[index + 1]->nKeys == M - 1) {
// 如果该子节点已经满了,则需要分裂该子节点
splitChild(currentNode, index + 1, currentNode->children[index + 1]);
if (currentNode->keys[index + 1] < key) {
index++;
}
}
insertNonFull(currentNode->children[index + 1], key);
}
}
// 获取指定节点上的前驱节点
int getPredecessor(struct node* currentNode, int index) {
struct node* currentNodeTmp = currentNode->children[index];
while (!currentNodeTmp->leaf) {
currentNodeTmp = currentNodeTmp->children[currentNodeTmp->nKeys];
}
return currentNodeTmp->keys[currentNodeTmp->nKeys - 1];
}
// 获取指定节点上的后继节点
int getSuccessor(struct node* currentNode, int index) {
struct node* currentNodeTmp = currentNode->children[index + 1];
while (!currentNodeTmp->leaf) {
currentNodeTmp = currentNodeTmp->children[0];
}
return currentNodeTmp->keys[0];
}
// 和其右兄弟合并
void mergeNodes(struct node* currentNode, int index) {
struct node* childNode = currentNode->children[index];
struct node* rightSiblingNode = currentNode->children[index + 1];
childNode->keys[M / 2 - 1] = currentNode->keys[index];
for (int i = 0; i < rightSiblingNode->nKeys; i++) {
childNode->keys[i + M / 2] = rightSiblingNode->keys[i];
}
if (!childNode->leaf) {
for (int i = 0; i <= rightSiblingNode->nKeys; i++) {
childNode->children[i + M / 2] = rightSiblingNode->children[i];
}
}
for (int i = index + 1; i < currentNode->nKeys; i++) {
currentNode->keys[i - 1] = currentNode->keys[i];
}
for (int i = index + 2; i <= currentNode->nKeys; i++) {
currentNode->children[i - 1] = currentNode->children[i];
}
childNode->nKeys += rightSiblingNode->nKeys + 1;
currentNode->nKeys--;
free(rightSiblingNode);
}
// 从左兄弟节点中向指定子节点移动一个键
void borrowFromLeft(struct node* currentNode, int childIndex) {
struct node* childNode = currentNode->children[childIndex];
struct node* leftSiblingNode = currentNode->children[childIndex - 1];
for (int i = childNode->nKeys - 1; i >= 0; i--) {
childNode->keys[i + 1] = childNode->keys[i];
}
if (!childNode->leaf) {
for (int i = childNode->nKeys; i >= 0; i--) {
childNode->children[i + 1] = childNode->children[i];
}
}
childNode->keys[0] = currentNode->keys[childIndex - 1];
if (!leftSiblingNode->leaf) {
childNode->children[0] = leftSiblingNode->children[leftSiblingNode->nKeys];
}
currentNode->keys[childIndex - 1] = leftSiblingNode->keys[leftSiblingNode->nKeys - 1];
childNode->nKeys++;
leftSiblingNode->nKeys--;
}
// 从右兄弟节点中向指定子节点移动一个键
void borrowFromRight(struct node* currentNode, int childIndex) {
struct node* childNode = currentNode->children[childIndex];
struct node* rightSiblingNode = currentNode->children[childIndex + 1];
childNode->keys[childNode->nKeys] = currentNode->keys[childIndex];
if (!childNode->leaf) {
childNode->children[childNode->nKeys + 1] = rightSiblingNode->children[0];
}
currentNode->keys[childIndex] = rightSiblingNode->keys[0];
for (int i = 1; i < rightSiblingNode->nKeys; i++) {
rightSiblingNode->keys[i - 1] = rightSiblingNode->keys[i];
}
if (!rightSiblingNode->leaf) {
for (int i = 1; i <= rightSiblingNode->nKeys; i++) {
rightSiblingNode->children[i - 1] = rightSiblingNode->children[i];
}
}
rightSiblingNode->nKeys--;
childNode->nKeys++;
}
// 在指定节点的子节点中移动一个键,以确保最少的键
void fillChild(struct node* currentNode, int childIndex) {
if (childIndex != 0 && currentNode->children[childIndex - 1]->nKeys >= M / 2) {
// 如果子节点的左兄弟节点有M/2个或更多键,则从左兄弟节点中向该子节点移动一个键
borrowFromLeft(currentNode, childIndex);
} else if (childIndex != currentNode->nKeys && currentNode->children[childIndex + 1]->nKeys >= M / 2) {
// 如果子节点的右兄弟节点具有M/2个或更多键,则从右兄弟节点中向该子节点移动一个键
borrowFromRight(currentNode, childIndex);
} else {
// 如果该子节点和其所有相邻兄弟节点都只有M/2-1个键,则将该子节点与其中一个相邻兄弟节点合并
if (childIndex != currentNode->nKeys) {
mergeNodes(currentNode, childIndex);
} else {
mergeNodes(currentNode, childIndex - 1);
}
}
}
// 删除节点上的某个键值对
void deleteKey(struct node* currentNode, int key) {
int index = findKeyIndex(currentNode, key);
if (index < currentNode->nKeys && currentNode->keys[index] == key) {
if (currentNode->leaf) {
// 如果节点是叶子节点并且包括该键,则删除该键
for (int i = index; i < currentNode->nKeys - 1; i++) {
currentNode->keys[i] = currentNode->keys[i + 1];
}
currentNode->nKeys--;
} else {
// 如果节点是内部节点,则在子节点中找到与该键紧随其后的键,并用该键替换该键
if (currentNode->children[index]->nKeys >= M / 2) {
int predecessorKey = getPredecessor(currentNode, index);
currentNode->keys[index] = predecessorKey;
deleteKey(currentNode->children[index], predecessorKey);
} else if (currentNode->children[index + 1]->nKeys >= M / 2) {
int successorKey = getSuccessor(currentNode, index);
currentNode->keys[index] = successorKey;
deleteKey(currentNode->children[index + 1], successorKey);
} else {
// 如果子节点和其右兄弟节点都只有M/2-1个键,则将两个节点合并
mergeNodes(currentNode, index);
deleteKey(currentNode->children[index], key);
}
}
} else {
// 如果节点不是叶子节点,则在适当的子树上递归删除该键
if (currentNode->leaf) {
printf("指定键值 %d 不存在于树中。\n", key);
return;
}
bool flag = (index == currentNode->nKeys);
if (currentNode->children[index]->nKeys < M / 2) {
// 如果所选择的子节点只有M/2-1个键,则需要对该子节点进行向左移动或合并
fillChild(currentNode, index);
}
if (flag && index > currentNode->nKeys) {
deleteKey(currentNode->children[index - 1], key);
} else {
deleteKey(currentNode->children[index], key);
}
}
}
//在m阶B树中查找一个键值,如果找到返回true,否则返回false
bool search(struct node* currentNode, int key) {
int index = findKeyIndex(currentNode, key);
if (index < currentNode->nKeys && currentNode->keys[index] == key) {
return true;
}
return (!currentNode->leaf && search(currentNode->children[index], key));
}
// 打印B树中的元素
void traverse(struct node* currentNode) {
int i;
for (i = 0; i < currentNode->nKeys; i++) {
if (!currentNode->leaf) {
traverse(currentNode->children[i]);
}
printf(" %d", currentNode->keys[i]);
}
if (!currentNode->leaf) {
traverse(currentNode->children[i]);
}
}
// 主函数
int main() {
struct node* root = NULL;
root = createNode(true);
int choice, key;
while (1) {
printf("\n-----------------------------------------------");
printf("\nM阶B树\n");
printf("1.插入元素\n2.删除元素\n3.查找元素\n4.打印元素\n5.退出\n");
printf("请选择您要进行的操作:");
scanf("%d", &choice);
switch (choice) {
case 1:
printf("请输入要插入的元素:");
scanf("%d", &key);
if (search(root, key)) {
printf("该元素已存在于树中。\n");
break;
}
if (root->nKeys == M - 1) {
// 如果根节点已满,则需要创建新的根节点
struct node* newRootNode = createNode(false);
newRootNode->children[0] = root;
splitChild(newRootNode, 0, root);
insertNonFull(newRootNode, key);
root = newRootNode;
} else {
insertNonFull(root, key);
}
printf("元素 %d 已插入树中。\n", key);
break;
case 2:
printf("请输入要删除的元素:");
scanf("%d", &key);
if (!search(root, key)) {
printf("该元素不存在于树中。\n");
break;
}
deleteKey(root, key);
printf("元素 %d 已从树中删除。\n", key);
break;
case 3:
printf("请输入要查找的元素:");
scanf("%d", &key);
if (search(root, key)) {
printf("该元素存在于树中。\n");
} else {
printf("该元素不存在于树中。\n");
}
break;
case 4:
printf("树中的元素是:");
traverse(root);
printf("\n");
break;
case 5:
exit(0);
default:
printf("请输入正确的操作编号。\n");
}
}
return 0;
}