[转]B+树的结构和实现代码

本文提供了一个详细的B+树实现代码示例,该结构广泛应用于数据库索引中,如BerkerlyDB、SQLite和MySQL等数据库。文章通过具体代码展示了如何进行查找、插入和删除操作,对于理解B+树的数据结构及其实现非常有帮助。

B+树实现代码

来源:http://supercyber.139.com/article/253784.html

这个结构一般用于数据库的索引,综合效率非常高,像 Berkerly DB , sqlite , mysql 数据库都使用了这个算法处理索引。
如果想自己做个小型数据库,可能参考一下这个算法的实现,可能会对你有所帮助。

其中的注册很详细,不用再多说了。

/* btrees.h */
/*
*平衡多路树的一种重要方案。
*在1970年由R.Bayer和E.McCreight发明。
*/
#define M1
/* B树的阶,即非根节点中键的最小数目。
*有些人把阶定义为非根节点中子树的最大数目。
*/
typedef
int typekey;
typedef
struct btnode{ /* B-Tree节点 */
int d; /* 节点中键的数目 */
typekeyk[
2 * M]; /* */
char * v[ 2 * M]; /* */
struct btnode * p[ 2 * M + 1 ]; /* 指向子树的指针 */
}node,
* btree;
/*
*每个键的左子树中的所有的键都小于这个键,
*每个键的右子树中的所有的键都大于等于这个键。
*叶子节点中的每个键都没有子树。
*/

/* 当M等于1时也称为2-3树
*+----+----+
*|k0|k1|
*+-+----+----+---
*|p0|p1|p2|
*+----+----+----+
*/
extern int btree_disp; /* 查找时找到的键在节点中的位置 */
extern char * InsValue; /* 与要插的键相对应的值 */

extern btreesearch(typekey,btree);
extern btreeinsert(typekey,btree);
extern btreedelete(typekey,btree);
extern int height(btree);
extern int count(btree);
extern double payload(btree);
extern btreedeltree(btree);
/* endofbtrees.h */

/* ***************************************************** */
/* ***************************************************** */

/* btrees.c */
#include
#include
#include
" btrees.h "

btreesearch(typekey,btree);
btreeinsert(typekey,btree);
btreedelete(typekey,btree);
int height(btree);
int count(btree);
double payload(btree);
btreedeltree(btree);

static void InternalInsert(typekey,btree);
static void InsInNode(btree, int );
static void SplitNode(btree, int );
static btreeNewRoot(btree);

static void InternalDelete(typekey,btree);
static void JoinNode(btree, int );
static void MoveLeftNode(btreet, int );
static void MoveRightNode(btreet, int );
static void DelFromNode(btreet, int );
static btreeFreeRoot(btree);

static btreedelall(btree);
static void Error( int ,typekey);

int btree_disp; /* 查找时找到的键在节点中的位置 */
char * InsValue = NULL; /* 与要插的键相对应的值 */
static int flag; /* 节点增减标志 */
static int btree_level = 0 ; /* 多路树的高度 */
static int btree_count = 0 ; /* 多路树的键总数 */
static int node_sum = 0 ; /* 多路树的节点总数 */
static int level; /* 当前访问的节点所处的高度 */
static btreeNewTree; /* 在节点分割的时候指向新建的节点 */
static typekeyInsKey; /* 要插入的键 */

btreesearch(typekeykey,btreet)
{
int i,j,m;
level
= btree_level - 1 ;
while (level >= 0 ){
for (i = 0 ,j = t -> d - 1 ;it -> k[m]) ? (i = m + 1 ):(j = m));
if (key == t -> k){
btree_disp
= i;
return t;
}
if (key > t -> k) /* i==t->d-1时有可能出现 */
i
++ ;
t
= t -> p;
level
-- ;
}
return NULL;
}

btreeinsert(typekeykey,btreet)
{
level
= btree_level;
InternalInsert(key,t);
if (flag == 1 ) /* 根节点满之后,它被分割成两个半满节点 */
t
= NewRoot(t); /* 树的高度增加 */
return t;
}

void InternalInsert(typekeykey,btreet)
{
int i,j,m;

level
-- ;
if (level < 0 ){ /* 到达了树的底部:指出要做的插入 */
NewTree
= NULL; /* 这个键没有对应的子树 */
InsKey
= key; /* 导致底层的叶子节点增加键值+空子树对 */
btree_count
++ ;
flag
= 1 ; /* 指示上层节点把返回的键插入其中 */
return ;
}
for (i = 0 ,j = t -> d - 1 ;it -> k[m]) ? (i = m + 1 ):(j = m));
if (key == t -> k){
Error(
1 ,key); /* 键已经在树中 */
flag
= 0 ;
return ;
}
if (key > t -> k) /* i==t->d-1时有可能出现 */
i
++ ;
InternalInsert(key,t
-> p);

if (flag == 0 )
return ;
/* 有新键要插入到当前节点中 */
if (t -> d < 2 * M){ /* 当前节点未满 */
InsInNode(t,i);
/* 把键值+子树对插入当前节点中 */
flag
= 0 ; /* 指示上层节点没有需要插入的键值+子树,插入过程结束 */
}
else /* 当前节点已满,则分割这个页面并把键值+子树对插入当前节点中 */
SplitNode(t,i);
/* 继续指示上层节点把返回的键值+子树插入其中 */
}

/*
*把一个键和对应的右子树插入一个节点中
*/
void InsInNode(btreet, int d)
{
int i;
/* 把所有大于要插入的键值的键和对应的右子树右移 */
for (i = t -> d;i > d;i -- ){
t
-> k = t -> k[i - 1 ];
t
-> v = t -> v[i - 1 ];
t
-> p[i + 1 ] = t -> p;
}
/* 插入键和右子树 */
t
-> k = InsKey;
t
-> p[i + 1 ] = NewTree;
t
-> v = InsValue;
t
-> d ++ ;
}
/*
*前件是要插入一个键和对应的右子树,并且本节点已经满
*导致分割这个节点,插入键和对应的右子树,
*并向上层返回一个要插入键和对应的右子树
*/
void SplitNode(btreet, int d)
{
int i,j;
btreetemp;
typekeytemp_k;
char * temp_v;
/* 建立新节点 */
temp
= (btree)malloc( sizeof (node));
/*
*+---+--------+-----+-----+--------+-----+
*|0|......|M|M+1|......|2*M-1|
*+---+--------+-----+-----+--------+-----+
*|<-M+1->|<-M-1->|
*/
if (d > M){ /* 要插入当前节点的右半部分 */
/* 把从2*M-1到M+1的M-1个键值+子树对转移到新节点中,
*并且为要插入的键值+子树空出位置
*/
for (i = 2 * M - 1 ,j = M - 1 ;i >= d;i -- ,j -- ){
temp
-> k[j] = t -> k;
temp
-> v[j] = t -> v;
temp
-> p[j + 1 ] = t -> p[i + 1 ];
}
for (i = d - 1 ,j = d - M - 2 ;j >= 0 ;i -- ,j -- ){
temp
-> k[j] = t -> k;
temp
-> v[j] = t -> v;
temp
-> p[j + 1 ] = t -> p[i + 1 ];
}
/* 把节点的最右子树转移成新节点的最左子树 */
temp
-> p[ 0 ] = t -> p[M + 1 ];
/* 在新节点中插入键和右子树 */
temp
-> k[d - M - 1 ] = InsKey;
temp
-> p[d - M] = NewTree;
temp
-> v[d - M - 1 ] = InsValue;
/* 设置要插入上层节点的键和值 */
InsKey
= t -> k[M];
InsValue
= t -> v[M];

}
else { /* d<=M */
/* 把从2*M-1到M的M个键值+子树对转移到新节点中 */
for (i = 2 * M - 1 ,j = M - 1 ;j >= 0 ;i -- ,j -- ){
temp
-> k[j] = t -> k;
temp
-> v[j] = t -> v;
temp
-> p[j + 1 ] = t -> p[i + 1 ];
}
if (d == M) /* 要插入当前节点的正中间 */
/* 把要插入的子树作为新节点的最左子树 */
temp
-> p[ 0 ] = NewTree;
/* 直接把要插入的键和值返回给上层节点 */
else { /* (d/*把节点当前的最右子树转移成新节点的最左子树 */
temp
-> p[ 0 ] = t -> p[M];
/* 保存要插入上层节点的键和值 */
temp_k
= t -> k[M - 1 ];
temp_v
= t -> v[M - 1 ];
/* 把所有大于要插入的键值的键和对应的右子树右移 */
for (i = M - 1 ;i > d;i -- ){
t
-> k = t -> k[i - 1 ];
t
-> v = t -> v[i - 1 ];
t
-> p[i + 1 ] = t -> p;
}
/* 在节点中插入键和右子树 */
t
-> k[d] = InsKey;
t
-> p[d + 1 ] = NewTree;
t
-> v[d] = InsValue;
/* 设置要插入上层节点的键和值 */
InsKey
= temp_k;
InsValue
= temp_v;
}
}
t
-> d = M;
temp
-> d = M;
NewTree
= temp;
node_sum
++ ;
}

btreedelete(typekeykey,btreet)
{
level
= btree_level;
InternalDelete(key,t);
if (t -> d == 0 )
/* 根节点的子节点合并导致根节点键的数目随之减少,
*当根节点中没有键的时候,只有它的最左子树可能非空
*/
t
= FreeRoot(t);
return t;
}

void InternalDelete(typekeykey,btreet)
{
int i,j,m;
btreel,r;
int lvl;

level
-- ;
if (level < 0 ){
Error(
0 ,key); /* 在整个树中未找到要删除的键 */
flag
= 0 ;
return ;
}
for (i = 0 ,j = t -> d - 1 ;it -> k[m]) ? (i = m + 1 ):(j = m));
if (key == t -> k){ /* 找到要删除的键 */
if (t -> v != NULL)
free(t
-> v); /* 释放这个节点包含的值 */
if (level == 0 ){ /* 有子树为空则这个键位于叶子节点 */
DelFromNode(t,i);
btree_count
-- ;
flag
= 1 ;
/* 指示上层节点本子树的键数量减少 */
return ;
}
else { /* 这个键位于非叶节点 */
lvl
= level - 1 ;
/* 找到前驱节点 */
r
= t -> p;
while (lvl > 0 ){
r
= r -> p[r -> d];
lvl
-- ;
}
t
-> k = r -> k[r -> d - 1 ];
t
-> v = r -> v[r -> d - 1 ];
r
-> v[r -> d - 1 ] = NULL;
key
= r -> k[r -> d - 1 ];
}
}
else if (key > t -> k) /* i==t->d-1时有可能出现 */
i
++ ;
InternalDelete(key,t
-> p);
/* 调整平衡 */
if (flag == 0 )
return ;
if (t -> p -> d < M){
if (i == t -> d) /* 在最右子树中发生了删除 */
i
-- ; /* 调整最右键的左右子树平衡 */
l
= t -> p;
r
= t -> p[i + 1 ];
if (r -> d > M)
MoveLeftNode(t,i);
else if (l -> d > M)
MoveRightNode(t,i);
else {
JoinNode(t,i);
/* 继续指示上层节点本子树的键数量减少 */
return ;
}
flag
= 0 ;
/* 指示上层节点本子树的键数量没有减少,删除过程结束 */
}
}

/*
*合并一个节点的某个键对应的两个子树
*/
void JoinNode(btreet, int d)
{
btreel,r;
int i,j;
l
= t -> p[d];
r
= t -> p[d + 1 ];

/* 把这个键下移到它的左子树 */
l
-> k[l -> d] = t -> k[d];
l
-> v[l -> d] = t -> v[d];
/* 把右子树中的所有键值和子树转移到左子树 */
for (j = r -> d - 1 ,i = l -> d + r -> d;j >= 0 ;j -- ,i -- ){
l
-> k = r -> k[j];
l
-> v = r -> v[j];
l
-> p = r -> p[j];
}
l
-> p[l -> d + r -> d + 1 ] = r -> p[r -> d];
l
-> d += r -> d + 1 ;
/* 释放右子树的节点 */
free(r);
/* 把这个键右边的键和对应的右子树左移 */
for (i = d;i < t -> d - 1 ;i ++ ){
t
-> k = t -> k[i + 1 ];
t
-> v = t -> v[i + 1 ];
t
-> p[i + 1 ] = t -> p[i + 2 ];
}
t
-> d -- ;
node_sum
-- ;
}
/*
*从一个键的右子树向左子树转移一些键,使两个子树平衡
*/
void MoveLeftNode(btreet, int d)
{
btreel,r;
int m; /* 应转移的键的数目 */
int i,j;
l
= t -> p[d];
r
= t -> p[d + 1 ];
m
= (r -> d - l -> d) / 2 ;

/* 把这个键下移到它的左子树 */
l
-> k[l -> d] = t -> k[d];
l
-> v[l -> d] = t -> v[d];
/* 把右子树的最左子树转移成左子树的最右子树
*从右子树向左子树移动m-1个键+子树对
*/
for (j = m - 2 ,i = l -> d + m - 1 ;j >= 0 ;j -- ,i -- ){
l
-&g
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值