1、B+树索引的总体结构 |
图1 B+树的结点结构 ![]() |
2、B+树索引的叶结点 ①指针Pi(i=1,2,…,n-1)指向具有搜索码值Ki的一个文件记录或一个指针(存储)桶,桶中的每个指针指向具有搜索码值Ki的一个文件记录。指针桶只在文件不按搜索码顺序物理存储时才使用。指针Pn具有特殊的作用; ②每个叶结点最多可有n-1个搜索码值,最少也要有 ![]() ③由于各叶结点按照所含的搜索码值有一个线性顺序,所以就可以利用各个叶结点的指针Pn将叶结点按搜索码顺序链接在一起。这种排序能够高效地对文件进行顺序处理,而B+树索引的其他结构能够高效地对文件进行随机处理,如图2所示。 |
图2 B+树索引的叶结点结构示例 ![]() |
3、B+树索引的非叶结点 ①B+树索引的非叶结点形成叶结点上的一个多级(稀疏)索引; ②非叶结点的结构和叶结点的结构相同,即含有能够存储n-1个搜索码值和n个指针的存储单元的数据结构。只不过非叶结点中的所有指针都指向树中的结点; ③如果一个非叶结点有m个指针,则 ![]() |
图3 B+树索引的非叶结点结构 ![]() |
④在一个含有m个指针的非叶结点中,指针Pi(i=2,…,m-1)指向一棵子树,该子树的所有结点的搜索码值大于等于Ki-1而小于Ki。指针Pm指向子树中所含搜索码值大于等于Km-1的那一部分,而指针P1指向子树中所含搜索码值小于K1的那一部分,如图4所示。 |
图4 B+树索引的非叶结点中指针Pi的指向 ![]() |
4、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 */ |
B+树的结构和部分操作的实现
最新推荐文章于 2023-07-15 01:03:28 发布