数据结构:B树、B+树详解和C语言实现
B树的定义
什么是B树
- B树是专门为磁盘或其他直接存取的辅助存储设备设计的一类平衡查找树,可以实现O(logN)时间复杂度的存取操作。
- 不同于红黑树,B树的每个节点可以存放多个数据。根据每个节点存放数据的多少,可以把B树分为不同的阶数。
为什么要用B树
- B树查找数据速度快,和红黑树等平衡二叉查找树相当,同时由于B树的每个节点可以包含多个数据关键字,相当于对数的底变大,查找的深度变小,减少了对磁盘的存取操作的次数。
B树的性质
我们按照如下性质,来定义一棵非空的m阶B树(m>2,2阶B树相当于二叉平衡查找树):
- 每个非叶子节点中存放若干关键字数据,并且有若干指向儿子节点的指针。指针数目=关键字数目+1
- 根节点有最少1个,最多m-1个关键字,最少2个,最多m个子节点。
- 非根节点最少有[m/2](向上取整),最多m-1个关键字
- 每个节点中的关键字从左到右以非降序排列
- 每个关键字均不小于其左子节点的关键字,不大于其右子节点的所有关键字
- 每个叶子节点都具有相同的深度
B+树
- B+树在B树的基础上进行了改进,在把所有的附加数据信息都存在叶子节点中,而非叶子节点只是存放其每颗子树中最大的关键字和指针,从而大大减小了内部节点的空间占用,使得一个磁盘块可以容纳更多的内部节点,减少了磁盘IO操作的次数,提高效率。
- 并且B+树的叶子结点本身依关键字的大小自小而大顺序链接,相当于一个顺序链表。需要范围查找的时候,b+树只需遍历叶子节点链表即可,b树却需要重复地中序遍历。
B+树与B树的区别
- 在B+树中,具有n个关键字的结点只含有n棵子树,即每个关键字对应一棵子树;而在B树中,具有n个关键字的结点含有(n+1)棵子树。
- 在B+树中,每个结点(非根结点)关键字个数n的范围是[m/2向上取整,m](根结点:1<=n<=m),在B树中,每个结点(非根结点)关键字个数n的范围是[m/2向上取整-1,m-1](根结点:1<=n<=m-1)。
- 在B+树中,叶结点包含信息,所有非叶结点仅起到索引作用,非叶结点中的每个索引项只含有对应子树的最大关键字和指向该子树的指针,不含有该关键字对应记录的存储地址。
- 在B+树中,叶结点包含了全部关键字,即在非叶结点中出现的关键字也会出现在叶结点中;而在B树中,叶结点包含的关键字和其他结点包含的关键字是不重复的。
B+树的特点
通常在B+树中有两个头指针:一个指向根结点,另一个指向关键字最小的叶结点。因此,可以对B+树进行两种查找运算:一种是从最小关键字开始的顺序查找,另一种是从根结点开始,进行多路查找。
C语言实现
数据结构定义
#define ORDER 3 //B树的阶数
typedef int KeyType; //关键字数据类型
typedef struct BTNode //B树结点
{
int keynum; /// 结点中关键字的个数
KeyType key[ORDER-1]; /// 关键字数组,长度=阶数-1
struct BTNode* child[ORDER]; /// 孩子指针数组,长度=阶数
int isLeaf; /// 是否是叶子节点的标志
}BTNode;
typedef BTNode* BTree; ///定义BTree
创建B树
初始化一个空结点
BTree BTree_init()
{
BTree bt =(BTree)calloc(1,sizeof(BTNode));
bt->isLeaf = 1;
bt->keynum = 0;
bt->parent = NULL;
return bt;
}
根据给定的数据集合进行建树
void BTree_create(BTree *tree,const KeyType *data,int length)
{
//初始化一个B树节点
*tree = BTree_init();
//循环插入
for(int i = 0;i<length;i+&