B-tree详解及实现(C语言)

M阶的B-tree是一棵具有下列结构特性的树:
(1)树的根或者是一片树叶,或者其儿子树在2到M之间。
(2)除根外,所有非树叶节点的儿子数在[M/2]到M之间。(符号[]表示向上取整)
(3)所有树叶都在相同的深度上。
所有的数据都存储在树叶上。也有把数据存储在内部节点的,但我这里选择前者,内部节点只是用来索引。在每一个内部节点上皆含有指向该节点各个儿子的指针P1,P2...,PM和分别代表在子树P2,P3 ... PM中发现的最小关键字的值k1,k2 ... kM-1。当然有些指针是NULL。而其对应的ki则是未定义的。对于每一个节点,其子树P1中所有的关键字都小于子树P2的关键字,如此等等。另外我们还要求在(非根)树叶中关键字的个数也在[M/2]和M之间。下图是一棵4阶B-tree。

4阶B-tree的流行称呼是2-3-4树,而3阶称为2-3树。下面使用2-3树来介绍B-tree的插入与删除。

1. B-tree的插入
下面来演示B-tree的插入过程。初始状态如下:

下面插入18,正好不破坏树的结构:

插入19,发现树叶已经满了,而且兄弟节点也是满的。所以将该树叶的元素一分为二。更新其符节点的键,得到下图:

尝试插入4,发现树叶已经满了,但是其兄弟节点还没有满。所以将一个元素分给兄弟,更新父节点。得到下图:

尝试插入1,发现树叶已满,而且兄弟也满了。更糟糕的是,其父节点的子树数也满了。这个时候必须分裂节点。然后逐级往上修改节点,如果节点不满条件,必须沿着路劲一直修改的根。甚至是生成新的根。下面是这次插入的过程(橘色表示新生成的节点):


现在插入21,没有破坏树的结构,只是更新路径上的节点的键值。


现在插入65,就必须一直分裂节点一直到根处,发现根的元素也满,必须要生成新的根。下面的一组图描述了这个过程。




总结一下插入的过程:对于一般的M阶B树,当插入一个关键字使得该节点的关键字超过M。我们可以先查看相邻的兄弟节点中有没有未满的。如有,将一个关键字插入到兄弟节点中。如果兄弟节点都满了。那么必须分裂该节点。分裂的两个节点各有一半关键字。然而这使得父节点多出一个儿子,因此我们必须检查父节点是否已经具有M个儿子,如果是,那么这个父节点也要分裂成两个节点。我们重复这个过程直到找到一个父节点具有少于M个儿子。如果分裂了根节点,那么就必须生成一个新的根。注意,在这个过程中要更新节点信息。

2. B-tree的删除
B-tree的删除是这样的过程。从根节点一直寻找直到树叶节点,找到该关键字。如果这个关键字数目只有[M/2],那么将它除去之后关键字少于[M/2]了。此时有两种情况:
(1)相邻的兄弟节点具有大于[M/2]的关键字
取兄弟节点的一个关键字添加在该节点中。
(2)相邻的兄弟节点只有[M/2]个关键字。
这个时候两个节点就必须合并成一个节点。假如此时父亲节点只有[M/2]个儿子。那么父亲节点也要执行合并的操作。我们重复这个过程直到找到一个父节点,其父节点具有大于[M/2]个儿子。如果到最后,根节点之后一个儿子,那么删除根节点,其儿子成为新的根。

3. B-tree的实现
B-tree的实现我实际上是从B+ Tree中修改得来的。实际上Key[0]保存了第一棵子树的最小值,所以除了树叶之外,内部节点的key值是从1开始的,只有树叶从0开始。这样做在代码实现上使得代码更加清晰(因为我也写过内部节点Key从0开始),速度也更快(不用使用FindMin来寻找一棵树的最小值)。下面我贴出实现代码。关于B+ tree的代码我已经写好,之后再发一篇帖子。
3.1 头文件
//
//  MBTree.h
//  MBTree
//
//  Created by Wuyixin on 2017/8/4.
//  Copyright © 2017年 Coding365. All rights reserved.
//

#ifndef MBTree_h
#define MBTree_h

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#define M (4)
#define LIMIT_M_2 (M % 2 ? (M + 1)/2 : M/2)

typedef struct MBNode *MBTree,*Position;
typedef int KeyType;
struct MBNode{
    int KeyNum;/* 关键字个数 */
    KeyType Key[M + 1];/* 内部节点从1开始(实际上0的位置存储了第一棵子树的最小值),树叶节点从0开始 */
    MBTree Children[M + 1];/* 存储M个孩子节点 */
};

/* 初始化 */
extern MBTree Initialize();
/* 插入 */
extern MBTree Insert(MBTree T,KeyType Key);
/* 删除 */
extern MBTree Remove(MBTree T,KeyType Key);
/* 销毁 */
extern MBTree Destroy(MBTree T);
/* 遍历节点 */
extern void Travel(MBTree T);

#endif /* MBTree_h */

3.2 实现文件
//
//  MBTree.c
//  MBTree
//
//  Created by Wuyixin on 2017/8/4.
//  Copyright © 2017年 Coding365. All rights reserved.
//

#include "MBTree.h"


static KeyType Unavailable = INT_MIN;

/* 生成节点并初始化 */
static MBTree MallocNewNode(){
    MBTree NewNode;
    int i;
    NewNode = malloc(sizeof(struct MBNode));
    if (NewNode == NULL)
        exit(EXIT_FAILURE);
    
    
    i = 0;
    while (i < M + 1){
        NewNode->Key[i] = Unavailable;
        NewNode->Children[i] = NULL;
        i++;
    }
    NewNode->KeyNum = 0;
    
    return NewNode;
}

/* 初始化 */
extern MBTree Initialize(){
    
    MBTree T;
    if (M < (3)){
        printf("M最小等于3!");
        exit(EXIT_FAILURE);
        
    }
    /* 根结点 */
    T = MallocNewNode();
    
    return T;
}

/* 寻找一个兄弟节点,其存储的关键字未满,否则返回NULL */
static Position FindSibling(Position Parent,int i){
    Position Sibling;
    int Limit;
    
    Limit = M;
    
    Sibling = NULL;
    if (i == 0){
        if (Parent->Children[1]->KeyNum < Limit)
            Sibling = Parent->Children[1];
    }
    else if (Parent->Children[i - 1]->KeyNum < Limit)
        Sibling = Parent->Children[i - 1];
    else if (i + 1 < Parent->KeyNum && Parent->Children[i + 1]->KeyNum < Limit){
        Sibling = Parent->Children[i + 1];
    }
    
    return Sibling;
}

/* 查找兄弟节点,其关键字数大于M/2 ;没有返回NULL*/
static Position FindSiblingKeyNum_M_2(Position Parent,int i,int *j){
    int Limit;
    Position Sibling;
    Sibling = NULL;
    
    Limit = LIMIT_M_2;
    
    if (i == 0){
        if (Parent->Children[1]->KeyNum > Limit){
            Sibling = Parent->Children[1];
            *j = 1;
        }
    }
    else{
        if (Parent->Children[i - 1]->KeyNum > Limit){
            Sibling = Parent->Children[i - 1];
            *j = i - 1;
        }
        else if (i + 1 < Parent->KeyNum && Parent->Children[i + 1]->KeyNum > Limit){
            Sibling = Parent->Children[i + 1];
            *j = i + 1;
        }
        
    }
    
    return Sibling;
}

/* 当要对X插入Key的时候,i是X在Parent的位置,j是Key要插入的位置
 当要对Parent插入X节点的时候,i是要插入的位置,Key和j的值没有用
 */
static Position InsertElement(int isKey, Position Parent,Position X,KeyType Key,int i,int j){
    
    int k;
    if (isKey){
        /* 插入key */
        k = X->KeyNum - 1;
        while (k >= j){
            X->Key[k + 1] = X->Key[k];k--;
        }
        
        X->Key[j] = Key;
        
        if (Parent != NULL)
            Parent->Key[i] = X->Key[0];
        
        X->KeyNum++;
        
    }else{
        /* 插入节点 */
        
        k = Parent->KeyNum - 1;
        while (k >= i){
            Parent->Children[k + 1] = Parent->Children[k];
            Parent->Key[k + 1] = Parent->Key[k];
            k--;
        }
        Parent->Key[i] = X->Key[0];
        Parent->Children[i] = X;
        
        Parent->KeyNum++;
        
    }
    return X;
}


static Position RemoveElement(int isKey, Position Parent,Position X,int i,int j){
    
    int k,Limit;
    
    if (isKey){
        Limit = X->KeyNum;
        /* 删除key */
        k = j + 1;
        while (k < Limit){
            X->Key[k - 1] = X->Key[k];k++;
        }
        
        X->Key[X->KeyNum - 1] = Unavailable;
        
        Parent->Key[i] = X->Key[0];
        
        X->KeyNum--;
    }else{
        /* 删除节点 */
        Limit = Parent->KeyNum;
        k = i + 1;
        while (k < Limit){
            Parent->Children[k - 1] = Parent->Children[k];
            Parent->Key[k - 1] = Parent->Key[k];
            k++;
        }
        
        Parent->Children[Parent->KeyNum - 1] = NULL;
        Parent->Key[Parent->KeyNum - 1] = Unavailable;
        
        Parent->KeyNum--;
        
    }
    return X;
}

/* Src和Dst是两个相邻的节点,i是Src在Parent中的位置;
 将Src的元素移动到Dst中 ,n是移动元素的个数*/
static Position MoveElement(Position Src,Position Dst,Position Parent,int i,int n){
    KeyType TmpKey;
    Position Child;
    int j,SrcInFront;
    
    SrcInFront = 0;
    
    if (Src->Key[0] < Dst->Key[0])
        SrcInFront = 1;
    
    j = 0;
    /* 节点Src在Dst前面 */
    if (SrcInFront){
        if (Src->Children[0] != NULL){
            while (j < n) {
                Child = Src->Children[Src->KeyNum - 1];
                RemoveElement(0, Src, Child, Src->KeyNum - 1, Unavailable);
                InsertElement(0, Dst, Child, Unavailable, 0, Unavailable);
                j++;
            }
        }else{
            while (j < n) {
                TmpKey = Src->Key[Src->KeyNum -1];
                RemoveElement(1, Parent, Src, i, Src->KeyNum - 1);
                InsertElement(1, Parent, Dst, TmpKey, i + 1, 0);
                j++;
            }
            
        }
        
        Parent->Key[i + 1] = Dst->Key[0];
        
    }else{
        if (Src->Children[0] != NULL){
            while (j < n) {
                Child = Src->Children[0];
                RemoveElement(0, Src, Child, 0, Unavailable);
                InsertElement(0, Dst, Child, Unavailable, Dst->KeyNum, Unavailable);
                j++;
            }
            
        }else{
            while (j < n) {
                TmpKey = Src->Key[0];
                RemoveElement(1, Parent, Src, i, 0);
                InsertElement(1, Parent, Dst, TmpKey, i - 1, Dst->KeyNum);
                j++;
            }
            
        }
        
        Parent->Key[i] = Src->Key[0];
        
    }
    
    return Parent;
}

/* 分裂节点 */
static MBTree SplitNode(Position Parent,Position X,int i){
    int j,k,Limit;
    Position NewNode;
    
    NewNode = MallocNewNode();
    
    k = 0;
    j = X->KeyNum / 2;
    Limit = X->KeyNum;
    while (j < Limit){
        if (X->Children[0] != NULL){
            NewNode->Children[k] = X->Children[j];
            X->Children[j] = NULL;
        }
        NewNode->Key[k] = X->Key[j];
        X->Key[j] = Unavailable;
        NewNode->KeyNum++;X->KeyNum--;
        j++;k++;
    }
    
    if (Parent != NULL)
        InsertElement(0, Parent, NewNode, Unavailable, i + 1, Unavailable);
    else{
        /* 如果是X是根,那么创建新的根并返回 */
        Parent = MallocNewNode();
        InsertElement(0, Parent, X, Unavailable, 0, Unavailable);
        InsertElement(0, Parent, NewNode, Unavailable, 1, Unavailable);
        
        return Parent;
    }
    
    return X;
}

/* 合并节点,X只有一个关键字,S有大于或等于M/2个关键字*/
static Position MergeNode(Position Parent, Position X,Position S,int i){
    int Limit;
    
    /* S的关键字数目大于M/2 */
    if (S->KeyNum > LIMIT_M_2){
        /* 从S中移动一个元素到X中 */
        MoveElement(S, X, Parent, i,1);
    }else{
        /* 将X全部元素移动到S中,并把X删除 */
        Limit = X->KeyNum;
        MoveElement(X,S, Parent, i,Limit);
        RemoveElement(0, Parent, X, i, Unavailable);
        
        free(X);
        X = NULL;
    }
    
    return Parent;
}

static MBTree RecursiveInsert(MBTree T,KeyType Key,int i,MBTree Parent){
    int j,Limit;
    Position Sibling;
    
    /* 查找分支 */
    j = 0;
    while (j < T->KeyNum && Key >= T->Key[j]){
        /* 重复值不插入 */
        if (Key == T->Key[j])
            return T;
        j++;
    }
    if (j != 0 && T->Children[0] != NULL) j--;
    
    /* 树叶 */
    if (T->Children[0] == NULL)
        T = InsertElement(1, Parent, T, Key, i, j);
    /* 内部节点 */
    else
        T->Children[j] = RecursiveInsert(T->Children[j], Key, j, T);
    
    /* 调整节点 */
    
    Limit = M;
    
    if (T->KeyNum > Limit){
        /* 根 */
        if (Parent == NULL){
            /* 分裂节点 */
            T = SplitNode(Parent, T, i);
        }
        else{
            Sibling = FindSibling(Parent, i);
            if (Sibling != NULL){
                /* 将T的一个元素(Key或者Child)移动的Sibing中 */
                MoveElement(T, Sibling, Parent, i, 1);
            }else{
                /* 分裂节点 */
                T = SplitNode(Parent, T, i);
            }
        }
        
    }
    
    if (Parent != NULL)
        Parent->Key[i] = T->Key[0];
    
    
    return T;
}

/* 插入 */
extern MBTree Insert(MBTree T,KeyType Key){
    return RecursiveInsert(T, Key, 0, NULL);
}

static MBTree RecursiveRemove(MBTree T,KeyType Key,int i,MBTree Parent){
    
    int j,NeedAdjust;
    Position Sibling,Tmp;
    
    Sibling = NULL;
    
    /* 查找分支 */
    j = 0;
    while (j < T->KeyNum && Key >= T->Key[j]){
        if (Key == T->Key[j])
            break;
        j++;
    }
    
    if (T->Children[0] == NULL){
        /* 没找到 */
        if (Key != T->Key[j] || j == T->KeyNum)
            return T;
    }else
        if (j == T->KeyNum || Key < T->Key[j]) j--;
    
    
    
    /* 树叶 */
    if (T->Children[0] == NULL){
        T = RemoveElement(1, Parent, T, i, j);
    }else{
        T->Children[j] = RecursiveRemove(T->Children[j], Key, j, T);
    }
    
    NeedAdjust = 0;
    /* 树的根或者是一片树叶,或者其儿子数在2到M之间 */
    if (Parent == NULL && T->Children[0] != NULL && T->KeyNum < 2)
        NeedAdjust = 1;
    /* 除根外,所有非树叶节点的儿子数在[M/2]到M之间。(符号[]表示向上取整) */
    else if (Parent != NULL && T->Children[0] != NULL && T->KeyNum < LIMIT_M_2)
        NeedAdjust = 1;
    /* (非根)树叶中关键字的个数也在[M/2]和M之间 */
    else if (Parent != NULL && T->Children[0] == NULL && T->KeyNum < LIMIT_M_2)
        NeedAdjust = 1;
    
    /* 调整节点 */
    if (NeedAdjust){
        /* 根 */
        if (Parent == NULL){
            if(T->Children[0] != NULL && T->KeyNum < 2){
                Tmp = T;
                T = T->Children[0];
                free(Tmp);
                return T;
            }
            
        }else{
            /* 查找兄弟节点,其关键字数目大于M/2 */
            Sibling = FindSiblingKeyNum_M_2(Parent, i,&j);
            if (Sibling != NULL){
                MoveElement(Sibling, T, Parent, j, 1);
            }else{
                if (i == 0)
                    Sibling = Parent->Children[1];
                else
                    Sibling = Parent->Children[i - 1];
                
                Parent = MergeNode(Parent, T, Sibling, i);
                T = Parent->Children[i];
            }
        }
        
    }
    
    
    return T;
}

/* 删除 */
extern MBTree Remove(MBTree T,KeyType Key){
    return RecursiveRemove(T, Key, 0, NULL);
}

/* 销毁 */
extern MBTree Destroy(MBTree T){
    int i,j;
    if (T != NULL){
        i = 0;
        while (i < T->KeyNum + 1){
            Destroy(T->Children[i]);i++;
        }
        
        printf("Destroy:(");
        /* 树叶的Key从0开始,内部节点从1开始 */
        if (T->Children[0] == NULL)
            j = 0;
        else
            j = 1;
        while (j < T->KeyNum)/*  T->Key[i] != Unavailable*/
            printf("%d:",T->Key[j++]);
        printf(") ");
        free(T);
        T = NULL;
    }
    
    return T;
}

static void RecursiveTravel(MBTree T,int Level){
    int i;
    if (T != NULL){
        printf("  ");
        printf("[Level:%d]-->",Level);
        printf("(");
        
        /* 树叶的Key从0开始,内部节点从1开始 */
        if (T->Children[0] == NULL)
            i = 0;
        else
            i = 1;
        
        while (i < T->KeyNum)/*  T->Key[i] != Unavailable*/
            printf("%d:",T->Key[i++]);
        printf(")");
        
        Level++;
        
        i = 0;
        while (i <= T->KeyNum) {
            RecursiveTravel(T->Children[i], Level);
            i++;
        }
        
        
    }
}

/* 遍历 */
extern void Travel(MBTree T){
    RecursiveTravel(T, 0);
    printf("\n");
}


3.3 调用
//
//  main.c
//  MBTree
//
//  Created by Wuyixin on 2017/8/4.
//  Copyright © 2017年 Coding365. All rights reserved.
//

#include <stdio.h>
#include <time.h>
#include "MBTree.h"

int main(int argc, const char * argv[]) {
    MBTree T;
    T = Initialize();
    
    clock_t c1 = clock();
    int i = 50000000;
    while (i > 0)
        T = Insert(T, i--);
    i = 50000001;
    while (i < 100000000)
        T = Insert(T, i++);
    
    i = 100000000;
    while (i > 100)
        T = Remove(T, i--);
    
    Travel(T);
    Destroy(T);
    
    clock_t c2 = clock();
    
    printf("\n用时: %lu秒\n",(c2 - c1)/CLOCKS_PER_SEC);
    return 0;
}






  • 7
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
红黑是一种自平衡的二叉搜索,它可以保证在最坏情况下,插入、删除和查找操作的时间复杂度都是 O(log n)。下面是一份C语言红黑的初始化代码详解: ```c #include <stdio.h> #include <stdlib.h> // 定义红黑节点结构体 typedef struct rb_node { int data; // 存储的数据 int color; // 节点颜色,0表示黑色,1表示红色 struct rb_node *left; // 左子节点 struct rb_node *right;// 右子节点 struct rb_node *parent; // 父节点 } rb_node; // 定义红黑结构体 typedef struct rb_tree { rb_node *root; // 根节点 rb_node *nil; // 哨兵节点 } rb_tree; // 初始化哨兵节点 void init_nil(rb_tree *tree) { tree->nil = (rb_node *) malloc(sizeof(rb_node)); tree->nil->color = 0; // 哨兵节点为黑色 tree->nil->parent = NULL; tree->nil->left = NULL; tree->nil->right = NULL; } // 初始化红黑 void init_rb_tree(rb_tree *tree) { init_nil(tree); tree->root = tree->nil; } // 创建红黑节点 rb_node *create_rb_node(int data) { rb_node *node = (rb_node *) malloc(sizeof(rb_node)); node->data = data; node->color = 1; // 新节点为红色 node->left = NULL; node->right = NULL; node->parent = NULL; return node; } // 插入节点 void insert_rb_node(rb_tree *tree, rb_node *node) { rb_node *x = tree->root; rb_node *y = tree->nil; while (x != tree->nil) { y = x; if (node->data < x->data) { x = x->left; } else { x = x->right; } } node->parent = y; if (y == tree->nil) { tree->root = node; } else if (node->data < y->data) { y->left = node; } else { y->right = node; } node->left = tree->nil; node->right = tree->nil; node->color = 1; insert_fixup(tree, node); } // 修复插入操作后的红黑性质 void insert_fixup(rb_tree *tree, rb_node *node) { while (node->parent->color == 1) { if (node->parent == node->parent->parent->left) { rb_node *y = node->parent->parent->right; if (y->color == 1) { node->parent->color = 0; y->color = 0; node->parent->parent->color = 1; node = node->parent->parent; } else { if (node == node->parent->right) { node = node->parent; left_rotate(tree, node); } node->parent->color = 0; node->parent->parent->color = 1; right_rotate(tree, node->parent->parent); } } else { rb_node *y = node->parent->parent->left; if (y->color == 1) { node->parent->color = 0; y->color = 0; node->parent->parent->color = 1; node = node->parent->parent; } else { if (node == node->parent->left) { node = node->parent; right_rotate(tree, node); } node->parent->color = 0; node->parent->parent->color = 1; left_rotate(tree, node->parent->parent); } } } tree->root->color = 0; } // 左旋 void left_rotate(rb_tree *tree, rb_node *node) { rb_node *y = node->right; node->right = y->left; if (y->left != tree->nil) { y->left->parent = node; } y->parent = node->parent; if (node->parent == tree->nil) { tree->root = y; } else if (node == node->parent->left) { node->parent->left = y; } else { node->parent->right = y; } y->left = node; node->parent = y; } // 右旋 void right_rotate(rb_tree *tree, rb_node *node) { rb_node *y = node->left; node->left = y->right; if (y->right != tree->nil) { y->right->parent = node; } y->parent = node->parent; if (node->parent == tree->nil) { tree->root = y; } else if (node == node->parent->left) { node->parent->left = y; } else { node->parent->right = y; } y->right = node; node->parent = y; } // 中序遍历 void inorder_rb_tree(rb_node *node) { if (node != NULL) { inorder_rb_tree(node->left); printf("%d ", node->data); inorder_rb_tree(node->right); } } int main() { rb_tree tree; init_rb_tree(&tree); int arr[] = {11, 2, 14, 1, 7, 15, 5, 8, 4}; int len = sizeof(arr) / sizeof(arr[0]); for (int i = 0; i < len; i++) { rb_node *node = create_rb_node(arr[i]); insert_rb_node(&tree, node); } printf("中序遍历红黑:"); inorder_rb_tree(tree.root); printf("\n"); return 0; } ``` 以上是一份简单的红黑初始化代码,其中包含了红黑的初始化、节点插入、修复插入操作后的红黑性质、左旋、右旋、中序遍历等基本操作。在使用时,需要注意初始化哨兵节点,以及在创建新节点时将其颜色设置为红色。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值