b树的实现(c++)
2015-12-08 17:39
513 查看
转自:http://blog.chinaunix.net/uid-20196318-id-3030529.html
B树的定义
假设B树的度为t(t>=2),则B树满足如下要求:(参考算法导论)
(1) 每个非根节点至少包含t-1个关键字,t个指向子节点的指针;至多包含2t-1个关键字,2t个指向子女的指针(叶子节点的子女为空)。
(2) 节点的所有key按非降序存放,假设节点的关键字分别为K[1], K[2] … K
, 指向子女的指针分别为P[1], P[2]…P[n+1],其中n为节点关键字的个数。则有:
P[1] <= K[1] <= P[2] <= K[2] …..<= K
<= P[n+1] // 这里P
也指其指向的关键字
(3) 若根节点非空,则根节点至少包含两个子女;
(4) 所有的叶子节点都在同一层。
B树的搜索,search(root, target)
从root出发,对每个节点,找到大于或等于target关键字中最小的K[i],如果K[i]与target相等,则查找成功;否则在P[i]中递归搜索target,直到到达叶子节点,如仍未找到则说明关键字不在B树中,查找失败。
B树的插入,insert(root, target)
B树的插入需要沿着搜索的路径从root一直到叶节点,根据B树的规则,每个节点的关键字个数在[t-1, 2t-1]之间,故当target要加入到某个叶子时,如果该叶子节点已经有2t-1个关键字,则再加入target就违反了B树的定义,这时就需要对该叶子节点进行分裂,将叶子以中间节点为界,分成两个包含t-1个关键字的子节点,同时把中间节点提升到该叶子的父节点中,如果这样使得父节点的关键字个数超过2t-1,则要继续向上分裂,直到根节点,根节点的分裂会使得树加高一层。
上面的过程需要回溯,那么能否从根下降到叶节点后不回溯就能完成节点的插入呢?答案是肯定的,核心思想就是未雨绸缪,在下降的过程中,一旦遇到已满的节点(关键字个数为2t-1),就就对该节点进行分裂,这样就保证在叶子节点需要分裂时,其父节点一定是非满的,从而不需要再向上回溯。
B树的删除,delete(root, target)
在删除B树节点时,为了避免回溯,当遇到需要合并的节点时就立即执行合并,B树的删除算法如下:从root向叶子节点按照search规律遍历:
(1) 如果target在叶节点x中,则直接从x中删除target,情况(2)和(3)会保证当再叶子节点找到target时,肯定能借节点或合并成功而不会引起父节点的关键字个数少于t-1。
(2) 如果target在分支节点x中:
(a) 如果x的左分支节点y至少包含t个关键字,则找出y的最右的关键字prev,并替换target,并在y中递归删除prev。
(b) 如果x的右分支节点z至少包含t个关键字,则找出z的最左的关键字next,并替换target,并在z中递归删除next。
(c) 否则,如果y和z都只有t-1个关键字,则将targe与z合并到y中,使得y有2t-1个关键字,再从y中递归删除target。
(3) 如果关键字不在分支节点x中,则必然在x的某个分支节点p[i]中,如果p[i]节点只有t-1个关键字。
(a) 如果p[i-1]拥有至少t个关键字,则将x的某个关键字降至p[i]中,将p[i-1]的最大节点上升至x中。
(b) 如果p[i+1]拥有至少t个关键字,则将x个某个关键字降至p[i]中,将p[i+1]的最小关键字上升至x个。
(c) 如果p[i-1]与p[i+1]都拥有t-1个关键字,则将p[i]与其中一个兄弟合并,将x的一个关键字降至合并的节点中,成为中间关键字。
B树的实现
数据结构
/**
* @brief the degree of btree
* key per node: [M-1, 2M-1]
* child per node: [M, 2M]
*/
#define M 2 // M为B树的度
typedef struct btree_node {
int k[2*M-1];
struct btree_node *p[2*M];
int num;
bool is_leaf;
} btree_node;
创建B树
btree_node *btree_node_new()
{
btree_node *node = (btree_node *)malloc(sizeof(btree_node));
if(NULL == node) {
return NULL;
}
for(int i = 0; i < 2 * M -1; i++) { // 初始化key
node->k[i] = 0;
}
for(int i = 0; i < 2 * M; i++) { // 初始化pointer
node->p[i] = NULL;
}
node->num = 0;
node->is_leaf = true; // 默认为叶子
}
btree_node *btree_create()
{
btree_node *node = btree_node_new();
if(NULL == node) {
return NULL;
}
return node;
}
插入节点
// 当child满时,将其进行分裂,child = parent->p[pos]
int btree_split_child(btree_node *parent, int pos, btree_node *child)
{
// 创建新的节点
btree_node *new_child = btree_node_new();
if(NULL == new_child) {
return -1;
}
// 新节点的is_leaf与child相同,key的个数为M-1
new_child->is_leaf = child->is_leaf;
new_child->num = M - 1;
// 将child后半部分的key拷贝给新节点
for(int i = 0; i < M - 1; i++) {
new_child->k[i] = child->k[i+M];
}
// 如果child不是叶子,还需要把指针拷过去,指针比节点多1
if(false == new_child->is_leaf) {
for(int i = 0; i < M; i++) {
new_child->p[i] = child->p[i+M];
}
}
child->num = M - 1;
// child的中间节点需要插入parent的pos处,更新parent的key和pointer
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 += 1;
}
// 执行该操作时,node->num < 2M-1
void btree_insert_nonfull(btree_node *node, int target)
{
if(1 == node->is_leaf) { // 如果在叶子中找到,直接删除
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 += 1;
} else { // 沿着查找路径下降
int pos = node->num;
while(pos > 0 && target < node->k[pos-1]) {
pos--;
}
if(2 * M -1 == node->p[pos]->num) {
btree_split_child(node, pos, node->p[pos]); // 如果路径上有满节点则分裂
if(target > node->k[pos]) {
pos++;
}
}
btree_insert_nonfull(node->p[pos], target);
}
}
//插入入口
btree_node* btree_insert(btree_node *root, int target)
{
if(NULL == root) {
return NULL;
}
// 对根节点的特殊处理,如果根是满的,唯一使得树增高的情形
// 先申请一个新的
if(2 * M - 1 == root->num) {
btree_node *node = btree_node_new();
if(NULL == node) {
return root;
}
node->is_leaf = 0;
node->p[0] = root;
btree_split_child(node, 0, root);
btree_insert_nonfull(node, target);
return node;
} else {
btree_insert_nonfull(root, target);
return root;
}
}
删除节点
// 将y,root->k[pos], z合并到y节点,并释放z节点,y,z各有M-1个节点
void btree_merge_child(btree_node *root, int pos, btree_node *y, btree_node *z)
{
// 将z中节点拷贝到y的后半部分
y->num = 2 * M - 1;
for(int i = M; i < 2 * M - 1; i++) {
y->k[i] = z->k[i-M];
}
y->k[M-1] = root->k[pos]; // k[pos]下降为y的中间节点
// 如果z非叶子,需要拷贝pointer
if(false == z->is_leaf) {
for(int i = M; i < 2 * M; i++) {
y->p[i] = z->p[i-M];
}
}
// k[pos]下降到y中,更新key和pointer
for(int j = pos + 1; j < root->num; j++) {
root->k[j-1] = root->k[j];
root->p[j] = root->p[j+1];
}
root->num -= 1;
free(z);
}
// 删除入口
btree_node *btree_delete(btree_node *root, int target)
{
// 特殊处理,当根只有两个子女,切两个子女的关键字个数都为M-1时,合并根与两个子女
// 这是唯一能降低树高的情形
if(1 == root->num) {
btree_node *y = root->p[0];
btree_node *z = root->p[1];
if(NULL != y && NULL != z &&
M - 1 == y->num && M - 1 == z->num) {
btree_merge_child(root, 0, y, z);
free(root);
btree_delete_nonone(y, target);
return y;
} else {
btree_delete_nonone(root, target);
return root;
}
} else {
btree_delete_nonone(root, target);
return root;
}
}
// root至少有个t个关键字,保证不会回溯
void btree_delete_nonone(btree_node *root, int target)
{
if(true == root->is_leaf) { // 如果在叶子节点,直接删除
int i = 0;
while(i < root->num && target > root->k[i]) i++;
if(target == root->k[i]) {
for(int j = i + 1; j < 2 * M - 1; j++) {
root->k[j-1] = root->k[j];
}
root->num -= 1;
} else {
printf("target not found\n");
}
} else { // 在分支中
int i = 0;
btree_node *y = NULL, *z = NULL;
while(i < root->num && target > root->k[i]) i++;
if(i < root->num && target == root->k[i]) { // 如果在分支节点找到target
y = root->p[i];
z = root->p[i+1];
if(y->num > M - 1) {
// 如果左分支关键字多于M-1,则找到左分支的最右节点prev,替换target
// 并在左分支中递归删除prev,情况2(a)
int pre = btree_search_predecessor(y);
root->k[i] = pre;
btree_delete_nonone(y, pre);
} else if(z->num > M - 1) {
// 如果右分支关键字多于M-1,则找到右分支的最左节点next,替换target
// 并在右分支中递归删除next,情况2(b)
int next = btree_search_successor(z);
root->k[i] = next;
btree_delete_nonone(z, next);
} else {
// 两个分支节点数都为M-1,则合并至y,并在y中递归删除target,情况2(c)
btree_merge_child(root, i, y, z);
btree_delete(y, target);
}
} else { // 在分支没有找到,肯定在分支的子节点中
y = root->p[i];
if(i < root->num) {
z = root->p[i+1];
}
btree_node *p = NULL;
if(i > 0) {
p = root->p[i-1];
}
if(y->num == M - 1) {
if(i > 0 && p->num > M - 1) {
// 左邻接节点关键字个数大于M-1
btree_shift_to_right_child(root, i-1, p, y); //情况3(a)
} else if(i < root->num && z->num > M - 1) {
// 右邻接节点关键字个数大于M-1
btree_shift_to_left_child(root, i, y, z); // 情况3(b)
} else if(i > 0) {
btree_merge_child(root, i-1, p, y); // 情况3(c)
y = p;
} else {
btree_merge_child(root, i, y, z); // 情况3(c)
}
btree_delete_nonone(y, target);
} else {
btree_delete_nonone(y, target);
}
}
}
}
//寻找rightmost,以root为根的最大关键字
int btree_search_predecessor(btree_node *root)
{
btree_node *y = root;
while(false == y->is_leaf) {
y = y->p[y->num];
}
return y->k[y->num-1];
}
// 寻找leftmost,以root为根的最小关键字
int btree_search_successor(btree_node *root)
{
btree_node *z = root;
while(false == z->is_leaf) {
z = z->p[0];
}
return z->k[0];
}
// z向y借节点,将root->k[pos]下降至z,将y的最大关键字上升至root的pos处
void btree_shift_to_right_child(btree_node *root, int pos,
btree_node *y, btree_node *z)
{
z->num += 1;
for(int i = z->num -1; i > 0; i--) {
z->k[i] = z->k[i-1];
}
z->k[0]= root->k[pos];
root->k[pos] = y->k[y->num-1];
if(false == z->is_leaf) {
for(int i = z->num; i > 0; i--) {
z->p[i] = z->p[i-1];
}
z->p[0] = y->p[y->num];
}
y->num -= 1;
}
// y向借节点,将root->k[pos]下降至y,将z的最小关键字上升至root的pos处
void btree_shift_to_left_child(btree_node *root, int pos,
btree_node *y, btree_node *z)
{
y->num += 1;
y->k[y->num-1] = root->k[pos];
root->k[pos] = z->k[0];
for(int j = 1; j < z->num; j++) {
z->k[j-1] = z->k[j];
}
if(false == z->is_leaf) {
y->p[y->num] = z->p[0];
for(int j = 1; j <= z->num; j++) {
z->p[j-1] = z->p[j];
}
}
z->num -= 1;
}
插入与删除过程(图片为层序遍历的结果)
插入序列[18, 31, 12, 10, 15, 48, 45, 47, 50, 52, 23, 30, 20]
删除序列[15, 18, 23, 30, 31, 52, 50, 48, 47, 45, 20, 12, 10]
B+树
与B树不同的时,B+树的关键字都存储在叶子节点,分支节点均为索引,在实现上大致与B树类似,在几个细节稍有不同。
(1) 数据结构中增加prev,next指针,用于将叶子节点串成有序双向链表。
(2) 在节点分裂的时候,如果分裂的节点为叶子,则需要把中间节点保留在左(或右)边的分支上,并且需要更新prev和next。
(3) 在节点合的时候,如果合并的节点为叶子,不需要把跟节点下降为中间节点,并且需要更新prev和next。
(4) 在向邻接节点借节点时,借来的关键字并不是父节点的关键字,而是邻接点的关键字,并根据实际情况更新父节点的索引。
btree.rar C语言实现,因用到了bool变量,编译时请使用g++ -o btree btree.c
B树的定义
假设B树的度为t(t>=2),则B树满足如下要求:(参考算法导论)
(1) 每个非根节点至少包含t-1个关键字,t个指向子节点的指针;至多包含2t-1个关键字,2t个指向子女的指针(叶子节点的子女为空)。
(2) 节点的所有key按非降序存放,假设节点的关键字分别为K[1], K[2] … K
, 指向子女的指针分别为P[1], P[2]…P[n+1],其中n为节点关键字的个数。则有:
P[1] <= K[1] <= P[2] <= K[2] …..<= K
<= P[n+1] // 这里P
也指其指向的关键字
(3) 若根节点非空,则根节点至少包含两个子女;
(4) 所有的叶子节点都在同一层。
B树的搜索,search(root, target)
从root出发,对每个节点,找到大于或等于target关键字中最小的K[i],如果K[i]与target相等,则查找成功;否则在P[i]中递归搜索target,直到到达叶子节点,如仍未找到则说明关键字不在B树中,查找失败。
B树的插入,insert(root, target)
B树的插入需要沿着搜索的路径从root一直到叶节点,根据B树的规则,每个节点的关键字个数在[t-1, 2t-1]之间,故当target要加入到某个叶子时,如果该叶子节点已经有2t-1个关键字,则再加入target就违反了B树的定义,这时就需要对该叶子节点进行分裂,将叶子以中间节点为界,分成两个包含t-1个关键字的子节点,同时把中间节点提升到该叶子的父节点中,如果这样使得父节点的关键字个数超过2t-1,则要继续向上分裂,直到根节点,根节点的分裂会使得树加高一层。
上面的过程需要回溯,那么能否从根下降到叶节点后不回溯就能完成节点的插入呢?答案是肯定的,核心思想就是未雨绸缪,在下降的过程中,一旦遇到已满的节点(关键字个数为2t-1),就就对该节点进行分裂,这样就保证在叶子节点需要分裂时,其父节点一定是非满的,从而不需要再向上回溯。
B树的删除,delete(root, target)
在删除B树节点时,为了避免回溯,当遇到需要合并的节点时就立即执行合并,B树的删除算法如下:从root向叶子节点按照search规律遍历:
(1) 如果target在叶节点x中,则直接从x中删除target,情况(2)和(3)会保证当再叶子节点找到target时,肯定能借节点或合并成功而不会引起父节点的关键字个数少于t-1。
(2) 如果target在分支节点x中:
(a) 如果x的左分支节点y至少包含t个关键字,则找出y的最右的关键字prev,并替换target,并在y中递归删除prev。
(b) 如果x的右分支节点z至少包含t个关键字,则找出z的最左的关键字next,并替换target,并在z中递归删除next。
(c) 否则,如果y和z都只有t-1个关键字,则将targe与z合并到y中,使得y有2t-1个关键字,再从y中递归删除target。
(3) 如果关键字不在分支节点x中,则必然在x的某个分支节点p[i]中,如果p[i]节点只有t-1个关键字。
(a) 如果p[i-1]拥有至少t个关键字,则将x的某个关键字降至p[i]中,将p[i-1]的最大节点上升至x中。
(b) 如果p[i+1]拥有至少t个关键字,则将x个某个关键字降至p[i]中,将p[i+1]的最小关键字上升至x个。
(c) 如果p[i-1]与p[i+1]都拥有t-1个关键字,则将p[i]与其中一个兄弟合并,将x的一个关键字降至合并的节点中,成为中间关键字。
B树的实现
数据结构
/**
* @brief the degree of btree
* key per node: [M-1, 2M-1]
* child per node: [M, 2M]
*/
#define M 2 // M为B树的度
typedef struct btree_node {
int k[2*M-1];
struct btree_node *p[2*M];
int num;
bool is_leaf;
} btree_node;
创建B树
btree_node *btree_node_new()
{
btree_node *node = (btree_node *)malloc(sizeof(btree_node));
if(NULL == node) {
return NULL;
}
for(int i = 0; i < 2 * M -1; i++) { // 初始化key
node->k[i] = 0;
}
for(int i = 0; i < 2 * M; i++) { // 初始化pointer
node->p[i] = NULL;
}
node->num = 0;
node->is_leaf = true; // 默认为叶子
}
btree_node *btree_create()
{
btree_node *node = btree_node_new();
if(NULL == node) {
return NULL;
}
return node;
}
插入节点
// 当child满时,将其进行分裂,child = parent->p[pos]
int btree_split_child(btree_node *parent, int pos, btree_node *child)
{
// 创建新的节点
btree_node *new_child = btree_node_new();
if(NULL == new_child) {
return -1;
}
// 新节点的is_leaf与child相同,key的个数为M-1
new_child->is_leaf = child->is_leaf;
new_child->num = M - 1;
// 将child后半部分的key拷贝给新节点
for(int i = 0; i < M - 1; i++) {
new_child->k[i] = child->k[i+M];
}
// 如果child不是叶子,还需要把指针拷过去,指针比节点多1
if(false == new_child->is_leaf) {
for(int i = 0; i < M; i++) {
new_child->p[i] = child->p[i+M];
}
}
child->num = M - 1;
// child的中间节点需要插入parent的pos处,更新parent的key和pointer
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 += 1;
}
// 执行该操作时,node->num < 2M-1
void btree_insert_nonfull(btree_node *node, int target)
{
if(1 == node->is_leaf) { // 如果在叶子中找到,直接删除
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 += 1;
} else { // 沿着查找路径下降
int pos = node->num;
while(pos > 0 && target < node->k[pos-1]) {
pos--;
}
if(2 * M -1 == node->p[pos]->num) {
btree_split_child(node, pos, node->p[pos]); // 如果路径上有满节点则分裂
if(target > node->k[pos]) {
pos++;
}
}
btree_insert_nonfull(node->p[pos], target);
}
}
//插入入口
btree_node* btree_insert(btree_node *root, int target)
{
if(NULL == root) {
return NULL;
}
// 对根节点的特殊处理,如果根是满的,唯一使得树增高的情形
// 先申请一个新的
if(2 * M - 1 == root->num) {
btree_node *node = btree_node_new();
if(NULL == node) {
return root;
}
node->is_leaf = 0;
node->p[0] = root;
btree_split_child(node, 0, root);
btree_insert_nonfull(node, target);
return node;
} else {
btree_insert_nonfull(root, target);
return root;
}
}
删除节点
// 将y,root->k[pos], z合并到y节点,并释放z节点,y,z各有M-1个节点
void btree_merge_child(btree_node *root, int pos, btree_node *y, btree_node *z)
{
// 将z中节点拷贝到y的后半部分
y->num = 2 * M - 1;
for(int i = M; i < 2 * M - 1; i++) {
y->k[i] = z->k[i-M];
}
y->k[M-1] = root->k[pos]; // k[pos]下降为y的中间节点
// 如果z非叶子,需要拷贝pointer
if(false == z->is_leaf) {
for(int i = M; i < 2 * M; i++) {
y->p[i] = z->p[i-M];
}
}
// k[pos]下降到y中,更新key和pointer
for(int j = pos + 1; j < root->num; j++) {
root->k[j-1] = root->k[j];
root->p[j] = root->p[j+1];
}
root->num -= 1;
free(z);
}
// 删除入口
btree_node *btree_delete(btree_node *root, int target)
{
// 特殊处理,当根只有两个子女,切两个子女的关键字个数都为M-1时,合并根与两个子女
// 这是唯一能降低树高的情形
if(1 == root->num) {
btree_node *y = root->p[0];
btree_node *z = root->p[1];
if(NULL != y && NULL != z &&
M - 1 == y->num && M - 1 == z->num) {
btree_merge_child(root, 0, y, z);
free(root);
btree_delete_nonone(y, target);
return y;
} else {
btree_delete_nonone(root, target);
return root;
}
} else {
btree_delete_nonone(root, target);
return root;
}
}
// root至少有个t个关键字,保证不会回溯
void btree_delete_nonone(btree_node *root, int target)
{
if(true == root->is_leaf) { // 如果在叶子节点,直接删除
int i = 0;
while(i < root->num && target > root->k[i]) i++;
if(target == root->k[i]) {
for(int j = i + 1; j < 2 * M - 1; j++) {
root->k[j-1] = root->k[j];
}
root->num -= 1;
} else {
printf("target not found\n");
}
} else { // 在分支中
int i = 0;
btree_node *y = NULL, *z = NULL;
while(i < root->num && target > root->k[i]) i++;
if(i < root->num && target == root->k[i]) { // 如果在分支节点找到target
y = root->p[i];
z = root->p[i+1];
if(y->num > M - 1) {
// 如果左分支关键字多于M-1,则找到左分支的最右节点prev,替换target
// 并在左分支中递归删除prev,情况2(a)
int pre = btree_search_predecessor(y);
root->k[i] = pre;
btree_delete_nonone(y, pre);
} else if(z->num > M - 1) {
// 如果右分支关键字多于M-1,则找到右分支的最左节点next,替换target
// 并在右分支中递归删除next,情况2(b)
int next = btree_search_successor(z);
root->k[i] = next;
btree_delete_nonone(z, next);
} else {
// 两个分支节点数都为M-1,则合并至y,并在y中递归删除target,情况2(c)
btree_merge_child(root, i, y, z);
btree_delete(y, target);
}
} else { // 在分支没有找到,肯定在分支的子节点中
y = root->p[i];
if(i < root->num) {
z = root->p[i+1];
}
btree_node *p = NULL;
if(i > 0) {
p = root->p[i-1];
}
if(y->num == M - 1) {
if(i > 0 && p->num > M - 1) {
// 左邻接节点关键字个数大于M-1
btree_shift_to_right_child(root, i-1, p, y); //情况3(a)
} else if(i < root->num && z->num > M - 1) {
// 右邻接节点关键字个数大于M-1
btree_shift_to_left_child(root, i, y, z); // 情况3(b)
} else if(i > 0) {
btree_merge_child(root, i-1, p, y); // 情况3(c)
y = p;
} else {
btree_merge_child(root, i, y, z); // 情况3(c)
}
btree_delete_nonone(y, target);
} else {
btree_delete_nonone(y, target);
}
}
}
}
//寻找rightmost,以root为根的最大关键字
int btree_search_predecessor(btree_node *root)
{
btree_node *y = root;
while(false == y->is_leaf) {
y = y->p[y->num];
}
return y->k[y->num-1];
}
// 寻找leftmost,以root为根的最小关键字
int btree_search_successor(btree_node *root)
{
btree_node *z = root;
while(false == z->is_leaf) {
z = z->p[0];
}
return z->k[0];
}
// z向y借节点,将root->k[pos]下降至z,将y的最大关键字上升至root的pos处
void btree_shift_to_right_child(btree_node *root, int pos,
btree_node *y, btree_node *z)
{
z->num += 1;
for(int i = z->num -1; i > 0; i--) {
z->k[i] = z->k[i-1];
}
z->k[0]= root->k[pos];
root->k[pos] = y->k[y->num-1];
if(false == z->is_leaf) {
for(int i = z->num; i > 0; i--) {
z->p[i] = z->p[i-1];
}
z->p[0] = y->p[y->num];
}
y->num -= 1;
}
// y向借节点,将root->k[pos]下降至y,将z的最小关键字上升至root的pos处
void btree_shift_to_left_child(btree_node *root, int pos,
btree_node *y, btree_node *z)
{
y->num += 1;
y->k[y->num-1] = root->k[pos];
root->k[pos] = z->k[0];
for(int j = 1; j < z->num; j++) {
z->k[j-1] = z->k[j];
}
if(false == z->is_leaf) {
y->p[y->num] = z->p[0];
for(int j = 1; j <= z->num; j++) {
z->p[j-1] = z->p[j];
}
}
z->num -= 1;
}
插入与删除过程(图片为层序遍历的结果)
插入序列[18, 31, 12, 10, 15, 48, 45, 47, 50, 52, 23, 30, 20]
删除序列[15, 18, 23, 30, 31, 52, 50, 48, 47, 45, 20, 12, 10]
B+树
与B树不同的时,B+树的关键字都存储在叶子节点,分支节点均为索引,在实现上大致与B树类似,在几个细节稍有不同。
(1) 数据结构中增加prev,next指针,用于将叶子节点串成有序双向链表。
(2) 在节点分裂的时候,如果分裂的节点为叶子,则需要把中间节点保留在左(或右)边的分支上,并且需要更新prev和next。
(3) 在节点合的时候,如果合并的节点为叶子,不需要把跟节点下降为中间节点,并且需要更新prev和next。
(4) 在向邻接节点借节点时,借来的关键字并不是父节点的关键字,而是邻接点的关键字,并根据实际情况更新父节点的索引。
btree.rar C语言实现,因用到了bool变量,编译时请使用g++ -o btree btree.c
相关文章推荐
- N种方法使用C++调用C#.NET库
- C语言strcat()函数:连接字符串
- C语言里的puts()函数怎么用
- 【C++】UTF-8字符和GB2312字符相互转换函数
- 编译原理实验之语法分析(算符优先分析算法(C语言))
- C++之测试snmp的注册模块
- C++之IPC测试单元
- C语言程序设计MOOC视频开讲了
- C++之位测试练习的相关代码
- C++ 建设者继承
- c++中volatile关键字解释
- 推荐下载资源-VC++控件Datagrid
- Visual C++ Tips: 忽略 LNK4099 警告
- Effective C++:规定20: 宁pass-by-reference-to-const更换pass-by-value
- C语言基础--指针和数据存储
- C/C++与python互相调用
- c语言怎么会有这么多的作用域?
- 一个简单的三子棋游戏(c语言实现)
- c语言中递归函数真的好吗?
- c语言中对可变参数列表的简单理解