B树的原理在这篇博客https://blog.csdn.net/xiaoan08133192/article/details/115622370中进行了介绍,这里是B树的代码实现。B树的插入和删除函数,在遍历的时候都是使用不回溯的方法,插入操作在对a结点插入数据的时,已经保证了a的关键字个数小于2*M - 1,删除操作在对a结点删除数据的时,已经保证了a的关键字个数大于M - 1。如何保证这一点是代码的精髓
//M为B树的最小度
#define M 2
typedef struct btree_node{
int key[2 * M - 1];
struct btree_node* ptr[2 * M];
int num;
bool is_leaf;
}Btree;
//查找key关键字是否在B树中,如果在返回其所在结点
//时间复杂度为O(t*logn)
btree_node* search(btree_node* node, int key){
int num = node -> num;
for(int i = 0; i < num; i++){
if(key < node -> key[i]){
break;
}
}
if(key == node -> key[i]){
return node;
}
if(node -> is_leaf){
return NULL;
}
return search(node -> ptr[i], key);
}
//创建一棵B树,时间复杂度是O(1)
btree_node* create(){
btree_node* root = new btree_node();
root -> is_leaf = true;
root -> num = 0;
return root;
}
void bTreeSplitChild(btree_node* node, int i){
btree_node* rightChild = new btree_node();
btree_node* leftChild = node -> ptr[i];
rightChild -> is_leaf = leftChild -> is_leaf;
rightChild -> num = M - 1;
int j;
//移动node[i]结点key
for(j = 1; j <= M - 1; j++){
rightChild -> key[j - 1] = leftChild -> key[j - 1 + M];
}
//移动node[i]非叶子结点的指针
if(!leftChild -> is_leaf){
for(int j = 1; j <= M; j++){
rightChild -> key[j - 1] = leftChild -> key[j - 1 + M];
}
}
//设置node[i]结点的num
leftChild -> num = M - 1;
//移动node结点的key
for(j = node -> num - 1; j >= i; j--){
node -> key[j + 1] = node -> key[j];
}
node -> key[i] = leftChild -> key[M];
//移动node结点的指针
for(j = node -> num; j >= i; j--){
node -> ptr[j + 1] = node -> ptr[j];
}
node -> key[i] = leftChild -> key[M];
node -> ptr[i + 1] = rightChild;
//设置node结点的num和指针
node -> num++;
}
//在没有满的node结点插入key
void bTreeInsertNonfull(btree_node* node, int key){
int num = node ->num;
if(node ->is_leaf){
while(num >= 1 && key < node ->key[num - 1]){
node ->key[num] = node ->key[num - 1];
num--;
}
node ->key[num] = key;
node ->num++;
}else{//非叶子结点的情况下
while(num >= 1 && key < node ->key[num - 1]){
num--;
}
if(node ->ptr[num] ->num == 2 * M - 1){//下面要遍历的这个节点需要分裂
bTreeSplitChild(node, num);
//分裂之后,node结点多一个key,这一步的目的是看需要插入到新增key的左边还是右边
if(key > node ->key[num]){
num++;
}
}
bTreeInsertNonfull(node ->ptr[num], key);
}
}
btree_node* bTreeInsert(btree_node* root, int key){
//和二叉搜索树不同,b树高度的增加发生在顶部,而非底部
if(root -> num == 2 * M - 1){
btree_node* node = new btree_node();
node ->is_leaf = false;
node ->num = 0;
node ->ptr[0] = root;
bTreeSplitChild(node, 0);
root = node;//新的树根结点
}
bTreeInsertNonfull(root, key);
return root;
}
//node ->ptr[pos]和node ->ptr[pos + 1]合并
void btree_merge_child(btree_node* node, int pos){
btree_node* left = node ->ptr[pos];
btree_node* right = node ->ptr[pos + 1];
left ->key[M - 1] = node ->key[pos];
int i;
for(i = M; i < 2 * M - 1; i++){
left ->key[i] = right ->key[i - M];
}
//如果z非叶子,需要拷贝指针
if(false == right ->is_leaf){
for(i = M; i < 2 * M; i++){
left ->ptr[i] = right ->ptr[i - M];
}
}
left -> num = 2*M-1;
//处理node,这时node不可能是叶子结点
for(i = pos + 1; i < node ->num; i++){
node ->key[i - 1] = node ->key[i];
node ->ptr[i - 1] = node ->ptr[i];
if(i == node ->num - 1){
//处理最后一个指针
node ->ptr[node ->num - 1] = node ->ptr[node ->num];
}
}
node ->num--;
delete right;
}
//向root->ptr[pos - 1]结点借一个关键字
void btreeShiftLeftToRight(btree_node* root, int pos){
int i;
//移动root ->ptr[pos]结点中的key
for(i = root ->ptr[pos] ->num - 1; i > 0; i--){
root ->ptr[pos] ->key[i] = root ->ptr[pos] ->key[i - 1];
}
root ->ptr[pos] ->key[0] = root ->key[pos];
root ->key[pos] = root ->ptr[pos - 1] ->key[root ->ptr[pos - 1] ->num - 1];
//移动root ->ptr[pos]结点中的ptr
if(false == root ->ptr[pos] ->is_leaf){
for(i = root ->ptr[pos] ->num; i > 0; i--){
root ->ptr[pos] ->ptr[i] = root ->ptr[pos] ->ptr[i - 1];
}
root ->ptr[pos] ->ptr[0] = root ->ptr[pos - 1] ->ptr[root ->ptr[pos - 1] ->num];
}
root ->ptr[pos - 1] ->num -= 1;
root ->ptr[pos] ->num += 1;
}
//向root->ptr[pos + 1]结点借一个关键字
void btreeShiftRightToLeft(btree_node* root, int pos){
int i;
root ->ptr[pos] ->key[root ->ptr[pos] ->num] = root ->key[pos];
root ->key[pos] = root ->ptr[pos + 1] ->key[0];
root ->ptr[pos] ->ptr[root ->ptr[pos] ->num + 1] = root ->ptr[pos + 1] ->ptr[0];
root ->ptr[pos] ->num += 1;
//移动root->ptr[pos + 1]中的key和ptr
for(i = 1; i < root ->ptr[pos + 1] ->num - 1; i++){
root ->ptr[pos + 1] ->key[i - 1] = root ->ptr[pos + 1] ->key[i];
}
if(false == root ->ptr[pos + 1] ->is_leaf){
for(i = 1; i < root ->ptr[pos + 1] ->num; i++){
root ->ptr[pos + 1] ->ptr[i - 1] = root ->ptr[pos + 1] ->ptr[i];
}
}
root ->ptr[pos + 1] ->num -= 1;
}
//寻找以root为根的最大关键字
int btree_search_predecessor(btree_node *root)
{
while(false == root->is_leaf) {
root = root->ptr[root->num];
}
return root->key[root->num-1];
}
//寻找以root为根的最小关键字
int btree_search_successor(btree_node *root)
{
while(false == root->is_leaf) {
root = root->ptr[0];
}
return root->key[0];
}
void btree_delete_nonone(btree_node* root, int key){
int i,j;
if(true == root ->is_leaf){//如果在叶子结点
i = 0;
while(i < root ->num && key > root ->key[i]) i++;
if(key == root ->key[i]){
for(j = i + 1; j < 2*M - 1; j++){
root ->key[j - 1] = root ->key[j];
}
//叶子结点不需要处理指针
root ->num--;
}else{
cout << "no " << key << endl;
}
}else{//root不是叶子结点,这里分为两种情况
//一种是key在root结点,另一种是key不再root结点
i = 0;
while(i < root ->num && key > root ->key[i]) i++;
if(i < root ->num && key == root ->key[i]){
if(root ->ptr[i] ->num > M - 1){
//找key的前驱结点代替key,递归删除key的前驱结点
int pre = btree_search_predecessor(root ->ptr[i]);
root ->key[i] = pre;
btree_delete_nonone(root ->ptr[i], pre);
}else if(root ->ptr[i + 1] ->num > M - 1){
//找key的后继结点代替key,递归删除key的后继结点
int next = btree_search_successor(root ->ptr[i + 1]);
root ->key[i + 1] = next;
btree_delete_nonone(root ->ptr[i + 1], next);
}else{
// 两个分支节点数都为M-1,将两个节点合并,并在root ->ptr[i]中递归删除target,
btree_merge_child(root, i);
btree_delete(root ->ptr[i], key);
}
}else{
btree_node* leftbrother = NULL;
btree_node* rightbrother = NULL;
if(i < root ->num) rightbrother = root ->ptr[i+1];
if(i > 0) leftbrother = root ->ptr[i-1];
if(root ->ptr[i] ->num == M - 1){
if(i > 0 && leftbrother ->num > M - 1){
// 左邻接节点关键字个数大于M-1
btreeShiftLeftToRight(root, i);
}else if(i < root ->num && rightbrother ->num > M - 1){
// 右邻接节点关键字个数大于M-1
btreeShiftRightToLeft(root, i);
}else{
//选择一个相邻的节点合并
if(i > 0){//有左兄弟
btree_merge_child(root, i - 1);
root = root ->ptr[i - 1]; //合并之后,在哪里继续遍历
}else{
btree_merge_child(root, i);
root = root ->ptr[i];
}
btree_delete_nonone(root, key);
}
}else{
btree_delete_nonone(root ->ptr[i], key);
}
}
}
}
btree_node* btree_delete(btree_node* node, int key){
//当root只有一个关键字且两个子女的关键字个数都为M-1时,合并根与两个子女
//这是唯一能降低树高的情形
if(NULL != node && 1 == node -> num){
if(NULL != node ->ptr[0] && NULL != node ->ptr[1] && node ->ptr[0] ->num == M - 1 && node -> ptr[1] ->num == M - 1){
btree_merge_child(node, 0);
node = node ->ptr[0];
}
}
btree_delete_nonone(node, key);
return node;
}