1、这个结构一般用于数据库的索引,综合效率较 高。 另外还有一种与此类似的树结构叫B+树,像 Berkerly DB , sqlite , mysql 数据库都使用了 B+树算法处理 索引。 这两种处理索引的数据结构的不同之处: 1。B 树中同一键值不会出现多次,并且它有可能出现在叶 结点,也有可能出现在非叶结点中。而 B+树的键一定 会出现在叶结点中,并且有可能在非叶结点中也有可能重复出现,以维持 B+树的平衡。 2。因为 B 树键位置不定,且在整个树结构中只出现一次,虽然可以节 省存储空间,但使得在插入、删除操作复杂度明显增加。B+树相比来说是一种较好的折中。 3。B 树的查询效率与键在树中的
2、位置有关,最大时间复 杂度与 B+树相同(在叶结点的时候),最小时间复 杂度为 1(在根结点的时候)。而 B+树的时候复 杂度对某建成的树是固定的。 如果想自己做个小型数据库,可以参考一 下下面给出的 B 树算法的实现,可能会对你有所帮助。 其中的注册很详细,不用再多说了。 /* btrees.h */ /* * 平衡多路树 的一种重要 方案。 * 在 1970 年由 R. Bayer 和 E. McCreight 发明。 */ #define M 1 /* B 树的阶,即非根节点中键的最小 数目。 * 有些人把阶 定义为非根 节点中子树的最大数目。 */ typedef int typeke
3、y; typedef struct btnode /* B-Tree 节点 */ int d; /* 节点中键的数目 */ typekey k2*M; /* 键 */ char *v2*M; /* 值 */ struct btnode *p2*M+1; /* 指向子树的指针 */ node, *btree; /* * 每个键的左 子树中的所 有的键都小于这个键, * 每个键的右 子树中的所 有的键都大于等于这个键。 * 叶子节点中 的每个键都 没有子树。 */ /* 当 M 等于 1 时也称为 2-3 树 * +-+-+ * | k0 | k1 | * +-+-+-+- * | p0 | p1
4、 | 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); exte
5、rn 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
6、(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(
7、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; /* 多路树的键总数 */ sta
8、tic 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-km)?(i=m+1):(j=m); if (key = t-k i ) btr
9、ee_disp = i; return t; if (key t-k i ) /* i = t-d-1 时有可能出现 */ i+; t = t-p i ; 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
10、,j,m; level-; if (level d-1; i t-km)?(i=m+1):(j=m); if (key = t-k i ) Error(1,key); /* 键已经在树中 */ flag = 0; return; if (key t-k i ) /* i = t-d-1 时有可能出现 */ i+; InternalInsert(key, t-p i ); if (flag = 0) return; /* 有新键要插入到当前节点中 */ if (t-d d; i d; i-) t-k i = t-ki-1; t-v i = t-vi-1; t-pi+1 = t-p i ; /*
11、插入键和右子树 */ t-k i = InsKey; t-pi+1 = NewTree; t-v i = 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); /* * +-+-+-+-+-+-+
12、 * | 0 | | M | M+1 | |2*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-kj = t-k i ; temp-vj = t-v i ; temp-pj+1 = t-pi+1; for(i=d-1,j=d-M-2; j=0; i-,j-) temp-kj = t-k i ; temp-vj = t-v i ; te
13、mp-pj+1 = t-pi+1; /* 把节点的最右子树转移成新节点的最左子树 */ temp-p0 = t-pM+1; /* 在新节点中插入键和右子树 */ temp-kd-M-1 = InsKey; temp-pd-M = NewTree; temp-vd-M-1 = InsValue; /* 设置要插入上层节点的键和值 */ InsKey = t-kM; InsValue = t-vM; else /* d =0; i-,j-) temp-kj = t-k i ; temp-vj = t-v i ; temp-pj+1 = t-pi+1; if (d = M) /* 要插入当前 节点的
14、正中间 */ /* 把要插入的子树作为新节点的最左子树 */ temp-p0 = NewTree; /* 直接把要插入的键和值返回给上层节点 */ else /* (dp0 = t-pM; /* 保存要插入上层节点的键和值 */ temp_k = t-kM-1; temp_v = t-vM-1; /* 把所有大于要插入的键值的键和对应的右子树 右移 */ for(i=M-1; id; i-) t-k i = t-ki-1; t-v i = t-vi-1; t-pi+1 = t-p i ; /* 在节点中插入键和右子树 */ t-kd = InsKey; t-pd+1 = NewTree; t-
15、vd = 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 Int
16、ernalDelete(typekey key, btree t) int i,j,m; btree l,r; int lvl; level-; if (level d-1; i t-km)?(i=m+1):(j=m); if (key = t-k i ) /* 找到要删除的键 */ if (t-v i != NULL) free(t-v i ); /* 释放这个节点包含的值 */ if (level = 0) /* 有子树为空则这个键位于叶子节点 */ DelFromNode(t,i); btree_count-; flag = 1; /* 指示上层节点本子树的键数量减少 */ return
17、; else /* 这个键位于 非叶节点 */ lvl = level-1; /* 找到前驱节点 */ r = t-p i ; while (lvl 0) r = r-pr-d; lvl-; t-k i =r-kr-d-1; t-v i =r-vr-d-1; r-vr-d-1=NULL; key = r-kr-d-1; else if (key t-k i ) /* i = t-d-1 时有可能出 现 */ i+; InternalDelete(key,t-p i ); /* 调整平衡 */ if (flag = 0) return; if (t-p i -d d) /* 在最右子树中发生了删
18、除 */ i-; /* 调整最右键的左右子树平衡 */ l = t-p i ; r = t-pi+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-pd; r
19、 = t-pd+1; /* 把这个键下移到它的左子树 */ l-kl-d = t-kd; l-vl-d = t-vd; /* 把右子树中的所有键值和子树转移到左子树 */ for (j=r-d-1,i=l-d+r-d; j = 0 ; j-,i-) l-k i = r-kj; l-v i = r-vj; l-p i = r-pj; l-pl-d+r-d+1 = r-pr-d; l-d += r-d+1; /* 释放右子树的节点 */ free(r); /* 把这个键右边的键和对应的右子树左移 */ for (i=d; i d-1; i+) t-k i = t-ki+1; t-v i = t-v
20、i+1; t-pi+1 = t-pi+2; t-d-; node_sum-; /* * 从一个键的 右子树向左 子树转移一些键,使两个子树平衡 */ void MoveLeftNode(btree t, int d) btree l,r; int m; /* 应转移的键的数目 */ int i,j; l = t-pd; r = t-pd+1; m = (r-d - l-d)/2; /* 把这个键下移到它的左子树 */ l-kl-d = t-kd; l-vl-d = t-vd; /* 把右子树的最左子树转移成左子树的最右子树 * 从右子树向左子树移动 m-1 个键+ 子树对 */ for (j=
21、m-2,i=l-d+m-1; j = 0; j-,i-) l-k i = r-kj; l-v i = r-vj; l-p i = r-pj; l-pl-d+m = r-pm-1; /* 把右子树的最左键提升到这个键的位置上 */ t-kd = r-km-1; t-vd = r-vm-1; /* 把右子树中的所有键值和子树左移 m 个位置 */ r-p0 = r-pm; for (i=0; id-m; i+) r-k i = r-ki+m; r-v i = r-vi+m; r-p i = r-pi+m; r-pr-d-m = r-pr-d; l-d+=m; r-d-=m; /* * 从一个键的
22、左子树向右 子树转移一些键,使两个子树平衡 */ void MoveRightNode(btree t, int d) btree l,r; int m; /* 应转移的键的数目 */ int i,j; l = t-pd; r = t-pd+1; m = (l-d - r-d)/2; /* 把右子树中的所有键值和子树右移 m 个位置 */ r-pr-d+m=r-pr-d; for (i=r-d-1; i=0; i-) r-ki+m = r-k i ; r-vi+m = r-v i ; r-pi+m = r-p i ; /* 把这个键下移到它的右子树 */ r-km-1 = t-kd; r-vm
23、-1 = t-vd; /* 把左子树的最右子树转移成右子树的最左子树 */ r-pm-1 = l-pl-d; /* 从左子树向右子树移动 m-1 个键+子树对 */ for (i=l-d-1,j=m-2; j=0; j-,i-) r-kj = l-k i ; r-vj = l-v i ; r-pj = l-p i ; /* 把左子树的最右键提升到这个键的位置上 */ t-kd = l-k i ; t-vd = l-v i ; l-d-=m; r-d+=m; /* * 把一个键和 对应的右子 树从一个节点中删除 */ void DelFromNode(btree t, int d) int i;
24、 /* 把所有大于要删除的键值的键左移 */ for(i=d; i d-1; i+) t-k i = t-ki+1; t-v i = t-vi+1; t-d-; /* * 建立有两个 子树和一个 键的根节点 */ btree NewRoot(btree t) btree temp; temp = (btree)malloc(sizeof(node); temp-d = 1; temp-p0 = t; temp-p1 = NewTree; temp-k0 = InsKey; temp-v0 = InsValue; btree_level+; node_sum+; return(temp); /*
25、 * 释放根节点 ,并返回它的 最左子树 */ btree FreeRoot(btree t) btree temp; temp = t-p0; 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
26、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 d; i+) if (t-v i != NULL) free(t-v i ); if (level 0) for (i=0; id ; i+) t-p i =delall(t-p i ); free(t); return NULL; /* end of btrees.c */