B树的实现(2)插入

B树的插入都是在叶子结点进行的,由于B树的结点中关键码的个数是有限制的,最小度数为M的B树的结点个数是从M-1到2M-1个。比如下图是最小度数为2的B树(又称为2-3树),如下图所示,它的结点的个数就是1-3个。

先定位到要插入的位置,如果叶子结点的关键码个数还没达到上限,比如插入32,就比较简单,直接插入就行;如果叶子结点的关键码数到达了上限,就要分裂成 2个子结点,把中间的关键码往上放到父节点中。但有极端的情况,就是父结点也是满的,就需要再次分裂,可能最后要把根结点也分裂了。但是这种算法不太好实 现。
在《算法导论》中实现用的是另外一种思想,就是先分裂,在查找插入位置的过程中,如果发现有满的结点,就先把它分裂了,这就保证了在最后叶结点上插入数据的时候,这个叶结点的父结点总是不满的。下面我们看一个例子:

我们用逐个结点插入的方法创建一棵B树,结点顺序分别是{18, 31, 12, 10, 15, 48, 45, 47, 50, 52, 23, 30, 20},我们看看具体过程:
1.创建一个空的B树;
2.插入18,这时候是非满的,如下所示:


3.同理插入31和12,都比较简单,如下所示:


4.插入10,这时候根结点是满的,就要分裂,由于根结点比较特殊,没有父结点,就要单独处理,先生成一个空结点做为新的根结点,再进行分裂,如下所示:


5.再插入15,48,45,由于非满,直接插入,如下所示:


6.插入47,这次叶结点满了,就要先分裂,再插入,如下所示:

其他都是同样的道理,就不赘述了,下面是源码,加入到btree.c中,最后写了个main函数和一个广度优先显示树的方法,大家可以自己对比结果,代码的实现参照了《算法导论》和博客

http://hi.baidu.com/kurt023/blog/item/4c368d8b51c59ed3fc1f10cc.html

他博客里面已经实现了,只是在定义B树的时候指针数和关键码数成一样了,我于是自己重写了一下。

1//函数目的:分裂存储数达到最大的节点
2void btreeSplitChild(struct btnode *parent, int pos, struct btnode *child){
3    struct btnode *child2;
4    int i;
5    //为新分裂出的节点分配空间
6    child2 = allocateNode(child2);
7    //与被分裂点同级
8    child2->isleaf = child->isleaf;
9    //设置节点数
10    child2->keyNum = M-1;
11 
12    //复制数据
13    for(i=0; i
14        child2->k[i] = child->k[i+M];
15    //如果不是叶节点,复制指针
16    if(!child->isleaf)
17        for(i=0; i
18            child2->p[i] = child->p[i+M];
19    child->keyNum = M-1;
20 
21    //将中间数作为索引插入到双亲节点中
22    //插入点后面的关键字和指针都往后移动一个位置
23    for(i=parent->keyNum; i>pos; i--){
24        parent->k[i] = parent->k[i-1];
25        parent->p[i+1] = parent->p[i];
26    }
27    parent->k[pos] = child->k[M-1];
28    parent->keyNum++;
29    parent->p[pos+1] = child2;
30}
31 
32/* 函数目的:向非满的节点中插入一个数据
33 * 注意:插入前保证key在原来的B树中不存在
34 */
35void btreeInsertNoneFull(struct btnode *ptr, int data){
36    int i;
37    struct btnode *child;   //要插入结点的子结点
38    i = ptr->keyNum;
39    //如果是叶节点,直接插入数据
40    if(ptr->isleaf){
41        while((i>0) && (datak[i-1])){
42            ptr->k[i] = ptr->k[i-1];
43            i--;
44        }
45        //插入数据
46        ptr->k[i] = data;
47        ptr->keyNum++;
48    }
49    else{   //不是叶节点,找到数据应插入的子节点并插入
50        while((i>0) && (datak[i-1]))
51            i--;
52        child = ptr->p[i];
53        if(child->keyNum == 2*M-1){
54            btreeSplitChild(ptr, i, child);
55            if(data > ptr->k[i])
56                i++;
57        }
58        child = ptr->p[i];
59        btreeInsertNoneFull(child, data);   //在子树中递归
60    }
61}
62 
63/* 插入一个结点 */
64struct btnode * btreeInsert(struct btnode *root, int data){
65    struct btnode *new;
66    /* 检查是否根节点已满,如果已满,分裂并生成新的根节点 */
67    if(root->keyNum == 2*M-1){
68        new = allocateNode(new);
69        new->isleaf = 0;
70        new->keyNum = 0;
71        new->p[0] = root;
72        btreeSplitChild(new, 0, root);
73        btreeInsertNoneFull(new, data);
74        return new;
75    }
76    else{    //还没到最大数据数,直接插入
77        btreeInsertNoneFull(root, data);
78        return root;
79    }
80}
81 
82//函数目的:广度优先显示树
83void btreeDisplay(struct btnode *root){
84    int i, queueNum=0;
85    int j;
86    struct btnode *queue[20];
87    struct btnode *current;
88 
89    //加入队列
90    queue[queueNum] = root;
91    queueNum++;
92 
93    while(queueNum>0){
94        //出队
95        current = queue[0];
96        queueNum--;
97        //移出第一个元素后后面的元素往前移动一个位置
98        for(i=0; i
99            queue[i] = queue[i+1];
100        //显示节点
101        j = current->keyNum;
102        printf("[ ");
103        for(i=0; i
104            printf("%d ", current->k[i]);
105        }
106        printf("]  ");
107 
108        //子节点入队
109        if(current!=NULL && current->isleaf!=1){
110            for(i=0; i<=(current->keyNum); i++){
111                queue[queueNum] = current->p[i];
112                queueNum++;
113            }
114        }
115    }
116    printf("\n");
117}
118 
119int main()
120{
121    struct btnode *root;
122    int a[13] = {18, 31, 12, 10, 15, 48, 45, 47, 50, 52, 23, 30, 20};
123    int i;
124 
125    root = btreeCreate(root);
126    for(i=0; i<13; i++){
127        root = btreeInsert(root, a[i]);
128        btreeDisplay(root);
129    }
130 
131    return 0;
132}

运行结果:

同样一批关键码用不同算法生成的B树可能是不同的,比如4个关键码的结点[1,2,3,4]分裂的时候,把2或3放上去都可以;同样的算法插入顺序不同也可能不同。
附件中是源码,在Linux下编译通过。

转载于:https://my.oschina.net/tonyyang/blog/12710

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值