B-树可以看做是平衡二叉排序树的推广,是一种平衡的多路查找树,它在文件系统中很有用。
形式化定义:一颗m阶的B树是空树,或是满足下列特性的m叉树:
1、树中每个结点至多有m颗子树(m-1个关键字)。
2、根结点或者为叶结点或者至少有两颗子树。
3、根之外所有非终端结点至少有ceil(m/2)颗子树。其中ceil(x)为取大于等于x的最小整数。
4、非终端结点包含信息(n,A0,K1,A1,K2,。。。Kn,An) Ki为关键字,有序;Ai为指向子树根结点的指针,且Ai所指子树中所有结点关键字均介于Ki-1与Ki之间。(n<m).
5、叶子结点都出现在同一层次上,不带信息。相当于查找失败结点。
首先是辅助宏的定义:
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -1
#define UNDERFLOW -2
#define NULL 0
#define m 3 //B-树的阶
typedef int Status;
typedef int KeyType;
B-树的存储结构定义:
//B-树的存储结构定义
typedef struct BTNode
{
int keynum; //结点中关键字大小,即结点的大小
KeyType key[m+1]; //关键字向量,0号单元未用
struct BTNode *parent; //指向双亲结点
struct BTNode *ptr[m+1]; //子树指针向量
// Record *recptr[m+1];
}BTNode,*BTree;
B-树上的查找:类似二叉排序树的查找,从根结点出发,要指针搜索和在结点内进行顺序(或折半)查找,两个过程交叉进行。
在m阶B-树T中查找关键字K.返回结果r 若查找成功 ,则特征值tag=1,指针pt所指结点中第i个关键字等于K,否则特征值tag=0,等于K的关键字应插入在指针pt所指结点第i和第i+1个关键字之间.
首先看一下返回结果的定义:
typedef struct
{
BTNode *pt; //指向找到的结点
int i; //1..m在结点中的关键字序号
int tag; //1 查找成功 0 查找失败
}Result; //B-树的查找结果类型
然后是具体的查找过程的实现:
Result SearchBTree(BTree T,KeyType K)
{
/*在m阶B-树T中查找关键字K 返回结果r 若查找成功 ,则特征值tag=1,
指针pt所指结点中第i个关键字等于K,否则特征值tag=0,等于K的关键
字应插入在指针pt所指结点第i和第i+1个关键字之间*/
BTNode *p=T,*q=NULL; //初始化 p指向待查结点 q指向p的双亲
int i;
bool found=false;
Result r;
while(p&&!found)
{
i=Search(p,K);
if(i>0&&p->key[i]==K) //找到待查关键字
found=true;
else
{
q=p;
p=p->ptr[i];
}
}
if(found) //查找成功
{
r.pt=p;
r.i=i;
r.tag=1;
}
else //查找不成功 返回K的插入信息
{
r.pt=q;
r.i=i;
r.tag=0;
}
return r;
}
B-树查找性能的分析:查找时间主要花费在搜索结点(访问外存)上,即主要取决于B-树的深度。
问:最坏复杂度是什么?含N个关键字的m阶B-树可能达到的最大深度H是多少?
或者说深度为H的B-树中,至少含有多少个结点?
第一层 1个
第二层 2个
第三层 2 * ceil(m/2)个
第四层 2*ceil(m/2)^2个
第H+1层 2*ceil(m/2)^(H-1)个
假设m阶B-树的深度为H+1,由于第H+1层为叶子结点,而当前树中含有N个关键字,则叶子结点必大于等于N+1个。因此可以推得:
N+1 >= 2*ceil(m/2)^(H-1).
得: H<=logceil(m/2) ((N+1)/2)+1.
所以得到结论:在含有N个关键字的B-树上进行一次查找,需访问的结点个数不超过logceil(m/2) ((N+1)/2)+1.
由于水平有限,其他操作如插入、删除等都没有实现,等以后有机会再说吧。