B+树的结构和部分操作的实现

原创 2007年10月03日 02:24:00

1、B+树索引的总体结构
①B+树索引是一个多级索引,但是其结构不同于多级顺序索引;
②B+树索引采用平衡树结构,即每个叶结点到根的路径长度都相同;
③每个非叶结点有到n个子女,n对特定的树是固定的;
④B+树的所有结点结构都相同,它最多包含n-1个搜索码值K1、K2、…、Kn-1,以及n个指针P1、P2、…、Pn,每个结点中的搜索码值按次序存放,即如果i<j,那么Ki<Kj,如图1所示。

图1 B+树的结点结构
2、B+树索引的叶结点
①指针Pi(i=1,2,…,n-1)指向具有搜索码值Ki的一个文件记录或一个指针(存储)桶,桶中的每个指针指向具有搜索码值Ki的一个文件记录。指针桶只在文件不按搜索码顺序物理存储时才使用。指针Pn具有特殊的作用;
②每个叶结点最多可有n-1个搜索码值,最少也要有个搜索码值。各个叶结点中搜索码值的范围互不相交。要使B+树索引成为稠密索引,数据文件中的各搜索码值都必须出现在某个叶结点中且只能出现一次;
③由于各叶结点按照所含的搜索码值有一个线性顺序,所以就可以利用各个叶结点的指针Pn将叶结点按搜索码顺序链接在一起。这种排序能够高效地对文件进行顺序处理,而B+树索引的其他结构能够高效地对文件进行随机处理,如图2所示。
图2 B+树索引的叶结点结构示例
3、B+树索引的非叶结点
①B+树索引的非叶结点形成叶结点上的一个多级(稀疏)索引;
②非叶结点的结构和叶结点的结构相同,即含有能够存储n-1个搜索码值和n个指针的存储单元的数据结构。只不过非叶结点中的所有指针都指向树中的结点;
③如果一个非叶结点有m个指针,则≤m≤n。若m<n,则非叶结点中指针Pm之后的所有空闲空间作为预留空间,与叶结点的区别在于结点的最后一个指针Pm和Pn的位置与指向不同,如图3所示;
图3 B+树索引的非叶结点结构
④在一个含有m个指针的非叶结点中,指针Pi(i=2,…,m-1)指向一棵子树,该子树的所有结点的搜索码值大于等于Ki-1而小于Ki。指针Pm指向子树中所含搜索码值大于等于Km-1的那一部分,而指针P1指向子树中所含搜索码值小于K1的那一部分,如图4所示。
图4 B+树索引的非叶结点中指针Pi的指向
4、B+树索引的根结点
①根结点的结构也与叶结点相同;
②根结点包含的指针数可以小于。但是,除非整棵树只有一个结点,否则根结点必须至少包含两个指针。图5给出一个B+树结构的示意图。
图5 account关系的B+树索引结构

5、部分操作的C语言实现
/* btrees.h */
/*
* 平衡多路树的一种重要方案。
* 在 1970 年由 R. Bayer 和 E. McCreight 发明。
*/
#define M 1
/* B 树的阶,即非根节点中键的最小数目。
* 有些人把阶定义为非根节点中子树的最大数目。
*/
typedef int typekey;
typedef struct btnode { /* B-Tree 节点 */
int d; /* 节点中键的数目 */
typekey k[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 btree search(typekey, btree);
extern btree insert(typekey,btree);
extern btree delete(typekey,btree);
extern int height(btree);
extern int count(btree);
extern double payload(btree);
extern btree deltree(btree);
/* end of btrees.h */

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

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

btree search(typekey, btree);
btree insert(typekey,btree);
btree delete(typekey,btree);
int height(btree);
int count(btree);
double payload(btree);
btree deltree(btree);

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

static void InternalDelete(typekey, btree);
static void JoinNode(btree, int);
static void MoveLeftNode(btree t, int);
static void MoveRightNode(btree t, int);
static void DelFromNode(btree t, int);
static btree FreeRoot(btree);

static btree delall(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 btree NewTree; /* 在节点分割的时候指向新建的节点 */
static typekey InsKey; /* 要插入的键 */

btree search(typekey key, btree t)
{
int i,j,m;
level=btree_level-1;
while (level >= 0){
for(i=0, j=t->d-1; i t->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;
}

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

void InternalInsert(typekey key, btree t)
{
int i,j,m;

level--;
if (level < 0){ /* 到达了树的底部: 指出要做的插入 */
NewTree = NULL; /* 这个键没有对应的子树 */
InsKey = key; /* 导致底层的叶子节点增加键值+空子树对 */
btree_count++;
flag = 1; /* 指示上层节点把返回的键插入其中 */
return;
}
for(i=0, j=t->d-1; i t->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(btree t, 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(btree t, int d)
{
int i,j;
btree temp;
typekey temp_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++;
}

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

void InternalDelete(typekey key, btree t)
{
int i,j,m;
btree l,r;
int lvl;

level--;
if (level < 0) {
Error(0,key); /* 在整个树中未找到要删除的键 */
flag = 0;
return;
}
for(i=0, j=t->d-1; i t->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(btree t, int d)
{
btree l,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(btree t, int d)
{
btree l,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->k = r->k[j];
l->v = r->v[j];
l->p = r->p[j];
}
l->p[l->d+m] = r->p[m-1];
/* 把右子树的最左键提升到这个键的位置上 */
t->k[d] = r->k[m-1];
t->v[d] = r->v[m-1];
/* 把右子树中的所有键值和子树左移 m 个位置 */
r->p[0] = r->p[m];
for (i=0; id-m; i++) {
r->k = r->k[i+m];
r->v = r->v[i+m];
r->p = r->p[i+m];
}
r->p[r->d-m] = r->p[r->d];
l->d+=m;
r->d-=m;
}
/*
* 从一个键的左子树向右子树转移一些键,使两个子树平衡
*/
void MoveRightNode(btree t, int d)
{
btree l,r;
int m; /* 应转移的键的数目 */
int i,j;
l = t->p[d];
r = t->p[d+1];

m = (l->d - r->d)/2;
/* 把右子树中的所有键值和子树右移 m 个位置 */
r->p[r->d+m]=r->p[r->d];
for (i=r->d-1; i>=0; i--) {
r->k[i+m] = r->k;
r->v[i+m] = r->v;
r->p[i+m] = r->p;
}
/* 把这个键下移到它的右子树 */
r->k[m-1] = t->k[d];
r->v[m-1] = t->v[d];
/* 把左子树的最右子树转移成右子树的最左子树 */
r->p[m-1] = l->p[l->d];
/* 从左子树向右子树移动 m-1 个键+子树对 */
for (i=l->d-1,j=m-2; j>=0; j--,i--) {
r->k[j] = l->k;
r->v[j] = l->v;
r->p[j] = l->p;
}
/* 把左子树的最右键提升到这个键的位置上 */
t->k[d] = l->k;
t->v[d] = l->v;
l->d-=m;
r->d+=m;
}
/*
* 把一个键和对应的右子树从一个节点中删除
*/
void DelFromNode(btree t, int d)
{
int i;
/* 把所有大于要删除的键值的键左移 */
for(i=d; i < t->d-1; i++) {
t->k = t->k[i+1];
t->v = t->v[i+1];
}
t->d--;
}
/*
* 建立有两个子树和一个键的根节点
*/
btree NewRoot(btree t)
{
btree temp;
temp = (btree)malloc(sizeof(node));
temp->d = 1;
temp->p[0] = t;
temp->p[1] = NewTree;
temp->k[0] = InsKey;
temp->v[0] = InsValue;
btree_level++;
node_sum++;
return(temp);
}
/*
* 释放根节点,并返回它的最左子树
*/
btree FreeRoot(btree t)
{
btree temp;
temp = t->p[0];
free(t);
btree_level--;
node_sum--;
return temp;
}

void Error(int f,typekey key)
{
if (f)
printf("Btrees error: Insert %d!/n",key);
else
printf("Btrees error: delete %d!/n",key);
}

int height(btree t)
{
return btree_level;
}

int count(btree t)
{
return btree_count;
}
double payload(btree t)
{
if (node_sum==0)
return 1;
return (double)btree_count/(node_sum*(2*M));
}
btree deltree (btree t)
{
level=btree_level;
btree_level = 0;
return delall(t);

}
btree delall(btree t)
{
int i;
level--;
if (level >= 0) {
for (i=0; i < t->d; i++)
if (t->v != NULL)
free(t->v);
if (level > 0)
for (i=0; i<= t->d ; i++)
t->p=delall(t->p);
free(t);
}
return NULL;
}

/* end of btrees.c */
 

相关文章推荐

数据结构面试之五—二叉树的常见操作(递归实现部分

数据结构面试之五—二叉树的常见操作(递归实现部分) 题注:《面试宝典》有相关习题,但思路相对不清晰,排版有错误,作者对此参考相关书籍和自己观点进行了重写,供大家参考。 转载请注明:http...
  • mark555
  • mark555
  • 2014年03月08日 10:07
  • 452

数据结构面试之五—二叉树的常见操作(递归实现部分)

数据结构面试之五—二叉树的常见操作(递归实现部分) 题注:《面试宝典》有相关习题,但思路相对不清晰,排版有错误,作者对此参考相关书籍和自己观点进行了重写,供大家参考。 转载请注明:http://b...

【数据结构】平衡搜索树之---B树的算法实现

#include using namespace std; #ifndef __BTREE_H__ #define __BTREE_H__ template//设为三阶B树(每个数组三个关键字) ...

数据结构课设--用B树实现图书管理系统

此文章是分享一下上学期数据结构课程的课程设计,我选择的是以B树为数据结构,开发一个图书管理系统,B树的优点在于查询快,增删结点相对于链表或者顺序表效率更好,因此用来存储大量图书信息更加合适。(开发环境...

数据结构:神奇的B树实现解析(有图有代码有真相!!!)

一、B树引入 二叉搜索树、平衡二叉树、红黑树都是动态查找树,典型的二叉搜索树结构,查找的时间复杂度和树的高度相关O(log2N)。 1)数据杂乱无章-------线性查找--O(n) 2)数据有序...

[从头学数学] 第259节 Python实现数据结构:平衡多路搜索树(B-Tree)

剧情提要: 阿伟看到了一本比较有趣的书,是关于《计算几何》的,2008年由北清派出版。很好奇 它里面讲了些什么,就来看看啦。 正剧开始: 星历2016年09月08日 11:00:14,...

数据结构与算法——B树的C++实现

数据结构与算法——B树的C++实现

数据结构实现(1)——B树

本文介绍了B树的定义和基本性质,重点分析了B树的插入、删除操作,然后实现了一个C++版本。仅供网友参考。主要参考书籍《数据结构——用面向对象方法与C++语言描述》(清华大学出版社,殷人昆主编)、《数据...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:B+树的结构和部分操作的实现
举报原因:
原因补充:

(最多只允许输入30个字)