日期:2014-05-16  浏览次数:20491 次

B-树和B+树的应用:数据搜索和数据库索引


B-树

1 .B-树定义

B-树是一种平衡的多路查找树,它在文件系统中很有用。

定义:一棵m 阶的B-树,或者为空树,或为满足下列特性的m 叉树:
⑴树中每个结点至多有m 棵子树;
⑵若根结点不是叶子结点,则至少有两棵子树;
⑶除根结点之外的所有非终端结点至少有[m/2] 棵子树;
⑷所有的非终端结点中包含以下信息数据:

      (n,A0,K1,A1,K2,…,Kn,An)
其中:Ki(i=1,2,…,n)为关键码,且Ki<Ki+1,

           Ai 为指向子树根结点的指针(i=0,1,…,n),且指针Ai-1 所指子树中所有结点的关键码均小于Ki (i=1,2,…,n),An 所指子树中所有结点的关键码均大于Kn.

           n   为关键码的个数。
⑸所有的叶子结点都出现在同一层次上,并且不带信息(可以看作是外部结点或查找失败的结点,实际上这些结点不存在,指向这些结点的指针为空)。

 如一棵四阶B-树,其深度为4.

          


B-树的查找类似二叉排序树的查找,所不同的是B-树每个结点上是多关键码的有序表,在到达某个结点时,先在有序表中查找,若找到,则查找成功;否则,到按照对应的指针信息指向的子树中去查找,当到达叶子结点时,则说明树中没有对应的关键码。

在上图的B-树上查找关键字47的过程如下:

1)首先从更开始,根据根节点指针找到 *节点,因为 *a 节点中只有一个关键字,且给定值47 > 关键字35,则若存在必在指针A1所指的子树内。

2)顺指针找到 *c节点,该节点有两个关键字(43和 78),而43 < 47 < 78,若存在比在指针A1所指的子树中。

3)同样,顺指针找到 *g节点,在该节点找到关键字47,查找成功。

2. 查找算法

typedef int KeyType ;
#define m 5					/*B 树的阶,暂设为5*/
typedef struct Node{
	int keynum;				/* 结点中关键码的个数,即结点的大小*/
	struct Node *parent;	/*指向双亲结点*/ 
	KeyType	key[m+1];		/*关键码向量,0 号单元未用*/ 
	struct Node *ptr[m+1];	/*子树指针向量*/ 
	Record *recptr[m+1];	/*记录指针向量*/
}NodeType;					/*B 树结点类型*/

typedef struct{
	NodeType *pt;			/*指向找到的结点*/
	int i;					/*在结点中的关键码序号,结点序号区间[1…m]*/
	int tag;				/* 1:查找成功,0:查找失败*/
}Result;					/*B 树的查找结果类型*/

Result SearchBTree(NodeType *t,KeyType kx)
{ 
	/*在m 阶B 树t 上查找关键码kx,反回(pt,i,tag)。若查找成功,则特征值tag=1,*/
	/*指针pt 所指结点中第i 个关键码等于kx;否则,特征值tag=0,等于kx 的关键码记录*/
	/*应插入在指针pt 所指结点中第i 个和第i+1 个关键码之间*/
	p=t;q=NULL;found=FALSE;i=0; /*初始化,p 指向待查结点,q 指向p 的双亲*/
	while(p&&!found)
	{	n=p->keynum;i=Search(p,kx);			/*在p-->key[1…keynum]中查找*/
		if(i>0&&p->key[i]= =kx) found=TRUE; /*找到*/
		else {q=p;p=p->ptr[i];}
	}
	if(found) return (p,i,1);				/*查找成功*/
	else return (q,i,0);					/*查找不成功,反回kx 的插入位置信息*/
}


3.B-树的插入

  B-树的生成也是从空树起,逐个插入关键字而得。但由于B-树结点中的关键字个数必须≥ceil(m/2)-1,因此,每次插入一个关键字不是在树中添加一个叶子结点,而是首先在最低层的某个非终端结点中添加一个关键字,若该结点的关键字个数不超过m-1,则插入完成,否则要产生结点的“分裂”,

如图(a) 为3阶的B-树(图中略去F结点(即叶子结点)),假设需依次插入关键字30,26,85。



1) 首先通过查找确定插入的位置。由根*a 起进行查找,确定30应插入的在*d 节点中。由于*d 中关键字数目不超过2(即m-1),故第一个关键字插入完成:如(b)


2) 同样,通过查找确定关键字26亦应插入 *d. 由于*d节点关键字数目超过2,此时需要将 *d分裂成两个节点,关键字26及其前、后两个指针仍保留在 *d 节点中,而关键字37 及其前、后两个指针存储到新的产生的节点 *d` 中。同时将关键字30 和指示节点 *d `的指针插入到其双亲的节点中。由于 *b节点中的关键字数目没有超过2,则插入完成.如(c)(d)




3) (e) -(g) 为插入85后;