在本教程中,您将学习什么是B树。此外,您还找到C语言中的示例。
B-树是一种特殊类型的自平衡搜索树,其中每个节点可以包含多个键,并且可以有两个以上的子节点。它是二叉搜索树的一种推广形式。
它也被称为高度平衡的m-way树。
1. 为什么选择B树?
随着访问物理存储介质(如硬盘)所需时间的减少,对B-tree的需求也随之增加。辅助存储设备速度较慢,容量较大,有必要采用这种类型的数据结构,以尽量减少对磁盘的访问。
其他数据结构如二叉搜索树、avl树、红黑树等只能在一个节点中存储一个key。如果您必须存储大量的key,那么这些树的高度会变得非常大,访问时间也会增加。
然而,B-树可以在单个节点中存储多个key,并且可以有多个子节点。这大大降低了高度,从而可以更快地访问磁盘。
2. B树属性
- 对于每个节点x,key按递增顺序存储。
- 在每个节点中,都有一个布尔值x.leaf,如果x是叶子,则该值为真。
- 如果n是树的顺序,则每个内部节点最多可以包含n-1个键以及指向每个子节点的指针。
- 除根节点外,每个节点最多可以有n个子节点,至少可以有n/2个子节点。
- 所有的叶子都有相同的深度(即树的高度h)。
- 根至少有2个子级,并且至少包含1个键。
- 如果n≥1,则对于任意n键B树,假设高度为h且最小级 t≥2,则 h ≥ log t ( n + 1 ) / 2 h ≥ \log_t (n+1)/2 h≥logt(n+1)/2。
3. 操作
3.1 搜索
在B-树中搜索元素是在二叉搜索树中搜索元素的一般形式,遵循以下步骤。
- 从根节点开始,将k与节点的第一个键进行比较。如果 k = 节点的第一个键,返回节点和相应索引。
- 如果k.leaf=true,则返回NULL(即未找到)。
- 如果 k < 根节点第一个键,递归搜索此键的左子级。
- 如果当前节点中有多个键且k>第一个键,请将k与节点中的下一个键进行比较。
如果k<下一个键,则搜索该键的左子项(即k位于第一个键和第二个键之间)。
否则,搜索键的右子级。 - 重复步骤1到4,直到到达叶。
3.2 搜索示例
- 让我们在3级以下的树中搜索关键字 k = 17。
- 在根中找不到k,因此请将其与根键进行比较。
- 因为k>11,所以转到根节点的右子节点。
- 把k和16比较一下。由于k>16,将k与下一个键18进行比较。
- 由于k<18,k介于16和18之间。搜索16的右子级或18的左子级。
- 找到k。
4. 搜索元素的算法
BtreeSearch(x, k)
i = 1
while i ≤ n[x] and k ≥ keyi[x] // n[x] means number of keys in x node
do i = i + 1
if i n[x] and k = keyi[x]
then return (x, i)
if leaf [x]
then return NIL
else
return BtreeSearch(ci[x], k)
5. C示例
// Searching a key on a B-tree in C
#include <stdio.h>
#include <stdlib.h>
#define MAX 3
#define MIN 2
struct BTreeNode {
int val[MAX + 1], count;
struct BTreeNode *link[MAX + 1];
};
struct BTreeNode *root;
// Create a node
struct BTreeNode *createNode(int val, struct BTreeNode *child) {
struct BTreeNode *newNode;
newNode = (struct BTreeNode *)malloc(sizeof(struct BTreeNode));
newNode->val[1] = val;
newNode->count = 1;
newNode->link[0] = root;
newNode->link[1] = child;
return newNode;
}
// Insert node
void insertNode(int val, int pos, struct BTreeNode *node,
struct BTreeNode *child) {
int j = node->count;
while (j > pos) {
node->val[j + 1] = node->val[j];
node->link[j + 1] = node->link[j];
j--;
}
node->val[j + 1] = val;
node->link[j + 1] = child;
node->count++;
}
// Split node
void splitNode(int val, int *pval, int pos, struct BTreeNode *node,
struct BTreeNode *child, struct BTreeNode **newNode) {
int median, j;
if (pos > MIN)
median = MIN + 1;
else
median = MIN;
*newNode = (struct BTreeNode *)malloc(sizeof(struct BTreeNode));
j = median + 1;
while (j <= MAX) {
(*newNode)->val[j - median] = node->val[j];
(*newNode)->link[j - median] = node->link[j];
j++;
}
node->count = median;
(*newNode)->count = MAX - median;
if (pos <= MIN) {
insertNode(val, pos, node, child);
} else {
insertNode(val, pos - median, *newNode, child);
}
*pval = node->val[node->count];
(*newNode)->link[0] = node->link[node->count];
node->count--;
}
// Set the value
int setValue(int val, int *pval,
struct BTreeNode *node, struct BTreeNode **child) {
int pos;
if (!node) {
*pval = val;
*child = NULL;
return 1;
}
if (val < node->val[1]) {
pos = 0;
} else {
for (pos = node->count;
(val < node->val[pos] && pos > 1); pos--)
;
if (val == node->val[pos]) {
printf("Duplicates are not permitted\n");
return 0;
}
}
if (setValue(val, pval, node->link[pos], child)) {
if (node->count < MAX) {
insertNode(*pval, pos, node, *child);
} else {
splitNode(*pval, pval, pos, node, *child, child);
return 1;
}
}
return 0;
}
// Insert the value
void insert(int val) {
int flag, i;
struct BTreeNode *child;
flag = setValue(val, &i, root, &child);
if (flag)
root = createNode(i, child);
}
// Search node
void search(int val, int *pos, struct BTreeNode *myNode) {
if (!myNode) {
return;
}
if (val < myNode->val[1]) {
*pos = 0;
} else {
for (*pos = myNode->count;
(val < myNode->val[*pos] && *pos > 1); (*pos)--)
;
if (val == myNode->val[*pos]) {
printf("%d is found", val);
return;
}
}
search(val, pos, myNode->link[*pos]);
return;
}
// Traverse then nodes
void traversal(struct BTreeNode *myNode) {
int i;
if (myNode) {
for (i = 0; i < myNode->count; i++) {
traversal(myNode->link[i]);
printf("%d ", myNode->val[i + 1]);
}
traversal(myNode->link[i]);
}
}
int main() {
int val, ch;
insert(8);
insert(9);
insert(10);
insert(11);
insert(15);
insert(16);
insert(17);
insert(18);
insert(20);
insert(23);
traversal(root);
printf("\n");
search(11, &ch, root);
}
6. B树的搜索复杂度
最坏情况时间复杂度:Θ(log n)
平均情况时间复杂度:Θ(log n)
最佳情况时间复杂度:Θ(log n)
平均情况空间复杂度:Θ(n)
最差情况空间复杂度:Θ(n)
7. B树应用
- 数据库和文件系统
- 存储数据块(辅助存储介质)
- 多级索引
参考文档
[1]Parewa Labs Pvt. Ltd.B-tree[EB/OL].https://www.programiz.com/dsa/b-tree,2020-01-01.