根据算法导论的描述。B树是一种特殊的平衡树和查找树。其主要用于磁盘内数据的存储和查找。因此,B树一般每个结点会比较大,包含许多数据关键字,最好占一个页面(page),这样存取的时候直接存取一个结点的数据。
B树的基本性质(M阶):
1、B树的每个叶子节点的高度均一致。
2、对于非空B树,普通非根非叶节点至少有M-1个关键字(M个子女),至多2M-1个关键字(2M个子女)。根节点至少包含2个子女(因为根节点只有1个关键字时,要有两个对应的孩子指针)。
3、结点内部的数据关键字是按从小到大排序的。
B树的结点的结构(M阶):
typedef struct btree_node
{
int k[2*M-1]; //用于存储数据关键字
btree_node *p[2*M]; //用于存储指向孩子的指针
int num; // 当前结点内数据关键字的个数, <=2*M-1
bool is_leaf; // 当前结点是否是叶子结点 true/false
}btree_node,*btree;
B树操作上的特质:
1、插入新数据关键字总是要往叶子结点上插入。
2、插入操作所造成的B树的扩张,使得树的高度升高一定是在根节点增加高度的。(这是由插入操作的手段所决定。)
B树结点的创建:
btree init_btree_node()
{
btree_node *node = (btree_node*)malloc(sizeof(btree_node));
if(node ==NULL)
return NULL;
for(int i=0;i<2*M-1;i++)
node->k[i] = 0;
for(int i=0;i<2*M;i++)
node->p[i] = NULL;
node->num = 0;
node->is_leaf = true;
return node;
}
//child结点满了,一分为二: 0~M-2 M-1 M~2M-2
// 1
//M-1 M-1
int split_child(btree parent, int pos, btree child)
{
//create a new child node for the other M-1 keys
btree_node *new_child = init_btree_node();
if(new_child == NULL)
return -1;
new_child->is_leaf = child->is_leaf;
new_child->num = M-1;
for(int i=0;i<M-1;i++)
new_child->k[i] = child->k[i+M];
if(new_child->is_leaf == false)
for(int i=0;i<M;i++)
new_child->p[i] = child->p[i+M];
//adjust the former child node
child->num = M-1;
//adjust parent node
for(int i=parent->num; i>pos;i--)
parent->p[i+1] = parent->p[i];
parent->p[pos+1] = new_child;
for(int i=parent->num-1;i>=pos;i--)
parent->k[i+1] = parent->k[i];
parent->k[pos] = child->k[M-1];
parent->num ++;
return 1;
}
对B树新关键字插入的实现是个特别的过程,从根结点向下追溯到叶子结点,途中遇到满的结点需要分裂:
--对根节点执行btree_insert();
--如果根节点满了,B树需要升高一层;
--向其孩子结点插入,遇到满结点需要分裂,逐步递归到叶子结点。
btree btree_insert(btree root, int target)
{
if(root == NULL)
return NULL;
if(root->num == 2*M-1)
{
//create new root
//the only way to add the height of btree
btree_node *node = init_btree_node();
if(node == NULL)
return NULL;
node->is_leaf = false;
node->p[0] = root;
split_child(node, 0, root);
btree_insert_nonfull(node, target);
return node;
}
else
{
btree_insert_nonfull(root, target);
return root;
}
}
void btree_insert_nonfull(btree node, int target)
{
if(node->is_leaf == true)
{
int pos = node->num;
while(pos>=1 && target < node->k[pos-1])
{
node->k[pos] = node->k[pos-1];
pos--;
}
node->k[pos] = target;
node->num ++;
}
else
{
int pos = node->num;
while(pos>0 && target < node->k[pos-1])
pos--;
if(node->p[pos]->num == 2*M-1)
{
split_child(node, pos, node->p[pos]);
if(target > node->k[pos])
pos++;
}
btree_insert_nonfull(node->p[pos], target);
}
}
B树的删除操作:
未完成,有待学习。