今天上完数据结构课,想找找事情做,可本人又懒得刷题(爱好:躺平)。就没事找事看看书上的扩展内容,发现这个b树挺有难度,要是能实现也成就感满满了呀。那便话不多说,撸起袖子加油干。
b树
看了一篇博客,讲的是真滴好,推荐:
http://blog.csdn.net/v_JULY_v/article/details/6530142
B树定义
B树是专门为磁盘或其他直接存取的辅助存储设备设计的一类平衡查找树,可以实现O(logN)时间复杂度的存取操作。
不同于红黑树,B树的每个节点可以存放多个数据。根据每个节点存放数据的多少,可以把B树分为不同的阶数。
B树好处
B树查找数据速度快,和红黑树等平衡二叉查找树相当,同时由于B树的每个节点可以包含多个数据关键字,
相当于对数的底变大,查找的深度变小,减少了对磁盘的存取操作的次数。
B树性质
我们按照如下性质,来定义一棵非空的m阶B树(m>2,2阶B树相当于二叉平衡查找树):
每个非叶子节点中存放若干关键字数据,并且有若干指向儿子节点的指针。指针数目=关键字数目+1
根节点有最少1个,最多m-1个关键字,最少2个,最多m个子节点。
非根节点最少有[m/2](向上取整),最多m-1个关键字
每个节点中的关键字从左到右以非降序排列
每个关键字均不小于其左子节点的关键字,不大于其右子节点的所有关键字
每个叶子节点都具有相同的深度
C语言实现b树
#include<stdio.h>
#include<stdlib.h>
#define ORDER 3 //B树的阶数
typedef int KeyType; //关键字数据类型
typedef struct BTNode //B树结点
{
int keynum; //结点中关键字的个数
KeyType key[ORDER-1]; // 关键字数组,长度=阶数-1
struct BTNode* child[ORDER]; // 孩子指针数组,长度=阶数
int isLeaf;
struct BTNode* parent; // 是否是叶子节点的标志
}BTNode,*BTree;
void BTree_init(BTNode **bt)//b树的初始化
{
(*bt)=(BTNode*)malloc(sizeof(BTNode));
(*bt)->isLeaf=1;
(*bt)->keynum=0;
(*bt)->parent=NULL;
}
void BTree_create(BTNode **tree,int length,int data[])//建立b树
{
BTree_init(&(*tree));//初始化一个B树节点
for(int i=0;i<length;i++)
{
BTree_insert(tree,data[i]);
}
}
///以下为插入环节
int isfull(BTNode *node)//判断节点是否是满节点
{
if(node->keynum < ORDER)
{
return 0;
}
else
{
return 1;
}
}
void BTree_insert(BTree *tree,KeyType key)//b树插入
{
BTNode *bnp = *tree;
//节点满,直接进行分裂
if(isfull(*tree))
{
split_tree(tree);
BTree_insert(tree,key);
return;
}
//是叶子节点且不满,直接插入
if(bnp->isLeaf && !isfull(bnp))
{
//比最大的关键字都大,直接插在末尾
if(key>=bnp->key[bnp->keynum -1])
{
bnp->keynum++;
bnp->key[bnp->keynum-1] = key;
}
else
{
for(int i = 0;i<ORDER-1;i++)
{
//找到一个比待插入关键字大的关键字,则直接插入
if(key< bnp->key[i] )
{
KeyType temp= bnp->key[i];
bnp->key[i] = key;
bnp->keynum++;
for(int j = i + 1;j< bnp->keynum;j++)
{
key = bnp->key[j];
bnp->key[j] = temp;
temp = key;
}
break;
}
}
}
return;
}
//不是叶子节点,查找对应的子树,递归插入
if(!bnp->isLeaf)
{
for(int i = 0;i < bnp ->keynum;i++)
{
if(key<bnp->key[i])
{
BTree_insert(&((*tree)->child[i]),key);
return;
}
}
BTree_insert(&((*tree)->child[bnp->keynum]),key);
}
}
void split_tree(BTree *tree)//子树分裂
{
BTree bnp1 = *tree;
BTree bnp2;
BTree_init(&(bnp2));
BTree bp;
int num = bnp1->keynum;
int split = num/2;
if(bnp1->parent == NULL)
{
BTree_init(&(bp));
bp->parent = NULL;
bp->keynum = 1;
bp->isLeaf = 0;
bp->key[0] = bnp1->key[split];
bp->child[0] = bnp1;
bp->child[1] = bnp2;
}
else
{
bp = bnp1->parent;
bp->isLeaf = 0;
bp->keynum++;
KeyType temp1, temp2;
BTNode *tcp1, *tcp2;
for(int i = 0;i < bp->keynum;i++)
{
//新关键字插到末尾
if(i == bp->keynum-1)
{
bp->key[i] = bnp1->key[split];
bp->child[i] = bnp1;
bp->child[i+1] = bnp2;
break;
}
//新关键字插到中间
if(bp->key[i]>bnp1->key[split])
{
tcp2 = bnp2;
temp2 = bnp1->key[split];
for(int k = i;k<bp->keynum;k++)
{
//关键字后移
temp1 = bp->key[k];
bp->key[k] = temp2;
temp2 = temp1;
//子树指针后移
tcp1 = bp->child[k+1];
bp->child[k+1] = tcp2;
tcp2 = tcp1;
}
}
}
}
bnp1->keynum = split;
bnp2->keynum = num - split -1;
for(int i = 0, j = split+1;j<num;j++)
{
bnp2->key[i]=bnp1->key[j];
bnp2->child[i]=bnp1->child[j];
}
bnp2->child[bnp2->keynum] = NULL;
bnp2->isLeaf = bnp1->isLeaf;
bnp2->parent = bp;
bnp1->parent = bp;
(*tree) = bp;
}
大体实现b树的插入,待进一步功能实现,今天先搞到这,有时间再完善。