B-树是一棵平衡的多路查找树,它在文件系统中很有用。
一棵m阶的B-树或为空树或为满足以下特性的m叉树:
-
树中的每个结点至多有m棵子树。
-
若根结点不是叶子结点,则至少有两颗子树。
-
除根结点外的所有非终端结点至少有Ceiling(m/2)(向上取整)棵子树
-
所有的非终端结点中包含下列信息数据:
(n, A0, K1, A1, K2, A2, … , Kn, An)- n表示有n个结点,Ki为关键字,Ai为指针。K1<…<Kn;指针Ai-1指向的结点的关键字均小于Ki,An所指向的结点的关键字均大于Kn。
-
所有的叶子结点均出现在同一层次上,且不带信息(可以看作外部结点或者查找失败的结点,实际上这些结点不存在,指向这些结点的指针为空)。
-
hpp
//
// BTree.hpp
// BTree
//
// Created by peiyu wang on 2019/3/25.
// Copyright © 2019 peiyu wang. All rights reserved.
//
#ifndef BTree_hpp
#define BTree_hpp
#include <stdio.h>
#include <cmath>
#include <iostream>
using namespace std;
/* 宏定义 */
#define M 3
/* 类型定义 */
typedef int BTElemType; //B树元素类型
typedef int KeyType;
struct BTNode
{
int keynum;
BTNode *parent; // 指向双亲结点
KeyType key[M+1]; // 关键字向量,0号单元未用
BTNode *ptr[M+1]; // 指向B树结点的指针
};
typedef BTNode* BTree;
/* B树查找结果类型 */
struct Result
{
BTree pt; // 指向找到的结点
int i; //1...n,关键字在结点中的序号(插入位置)
int tag; // 1:查找成功,0:查找失败
};
/* 函数列表 */
// 创建B树
int CreateBTree(BTree &BT);
// 查找并返回查找信息
Result SearchBTree(BTree BT, KeyType key);
// 返回key在结点p中的次序,若不存在则返回0
int Search(BTree p, KeyType key);
// 将关键字key插入到B树中
int InsertKey(BTree &BT, KeyType key);
// 插入,在结点q的key[i]和key[i+1]之间插入关键字key
int InsertBTree(BTree &BT, KeyType key, BTree q, int i);
// 将x和ap分别插入到q->key[i+1]和q->ptr[i+1]
void Insert(BTree q, KeyType i, KeyType x, BTree ap);
// 以s为界,将q指向的结点分裂成q和ap所指向的两部分
void split(BTree q, int s, BTree &ap);
// 生成含信息(BT,x,ap)的新的根结点BT, 原BT和ap为子树指针,q初值为NULL。
void NewRoot(BTree &BT, int x, BTree ap);
// 从B树中删除关键字key
int DeleteKey(BTree &BT, KeyType K);
// 从B树中删除结点q中第i个关键字
int DeleteBTree(BTree &BT, BTree q, int i);
// 从B树中删除结点q中第i个关键字(特殊)
void Delete(BTree &BT, BTree q, int i);
// 找出BT中最小的关键字
int SearchMinKey(BTree BT, Result &R);
// 找出双亲结点: q为p的第j个孩子
int FoundParent(BTree q, BTree &p, int &i);
// 向左移动关键字和指针
int LeftMove(BTree old_ptr, int m, BTree new_ptr, int n, int len);
// 向右移动关键字和指针
int RightMove(BTree old_ptr, int m, BTree new_ptr, int n, int len);
// 层序输出B树
void PrintBT_Level(BTree BT);
// 中序输出B树
void PrintBT_InOrder(BTree BT);
#endif /* BTree_hpp */
- cpp
//
// BTree.cpp
// BTree
//
// Created by peiyu wang on 2019/3/25.
// Copyright © 2019 peiyu wang. All rights reserved.
//
#include "BTree.hpp"
#include <queue>
// 创建B树
int CreateBTree(BTree &BT)
{
int n;
BT = NULL;
cout << "请输入结点数目:" << endl;
cin >> n;
cout << "依次输入每个结点的关键值:" << endl;
for (int i = 1; i <= n; i++)
{
KeyType key;
cin >> key;
InsertKey(BT, key);
}
return 1;
}
// 查找并返回查找信息
Result SearchBTree(BTree BT, KeyType key)
{
Result R = {NULL, 0, 0};
BTree p, q;
int found, i;
p = BT;
q = NULL;
found = false;
i = 0;
while (p && !found)
{
i = Search(p, key);
if (i > 0 && p->key[i] == key)
{
found = true;
}
else
{
q = p;
p = p->ptr[i];
}
}
R.i = i;
if (found)
{
R.pt = p;
R.tag = 1;
}
else
{
R.pt = q;
R.tag = 0;
}
return R;
}
// 返回key在结点p中的次序,若不存在则返回0
int Search(BTree p, KeyType key)
{
int i, j;
for (i = 0, j = 1; j <= p->keynum; j++)
{
if (p->key[j] <= key)
i = j;
else
break;
}
return i;
}
// 将关键字key插入到B树中
int InsertKey(BTree &BT, KeyType key)
{
Result R;
R = SearchBTree(BT, key);
if (R.tag == 0)
{
InsertBTree(BT, key, R.pt, R.i);
return 1;
}
else
return 0;
}
// 插入,在结点q的key[i]和key[i+1]之间插入关键字key
int InsertBTree(BTree &BT, KeyType key, BTree q, int i)
{
KeyType x;
BTree ap;
int finished;
int s;
x = key;
ap = NULL;
finished = false;
while (q && !finished)
{
Insert(q, i, x, ap);
if (q->keynum < M)
finished = true;
else
{
s = ceil((double)M / 2);
split(q, s, ap);
x = q->key[s];
q = q->parent;
if (q)
i = Search(q, x);
}
}
if (!finished)
NewRoot(BT, x, ap); //生成含信息(BT,x,ap)的新结点BT
return 1;
}
// 将x和ap分别插入到q->key[i+1]和q->ptr[i+1]
void Insert(BTree q, KeyType i, KeyType x, BTree ap)
{
int j;
for (j = q->keynum; j > i; j--)
{
q->key[j+1] = q->key[j];
q->ptr[j+1] = q->ptr[j];
}
q->key[i+1] = x;
q->ptr[i+1] = ap;
q->keynum++;
}
// 以s为界,将q指向的结点分裂成q和ap所指向的两部分
void split(BTree q, int s, BTree &ap)
{
ap = new BTNode;
ap->ptr[0] = q->ptr[s];
for (int i = s+1; i <= M; i++)
{
ap->key[i-s] = q->key[i];
ap->ptr[i-s] = q->ptr[i];
}
ap->keynum = M-s;
q->keynum = s-1;
ap->parent = q->parent;
for (int i = 0; i <= ap->keynum; i++)
{
if (ap->ptr[i])
ap->ptr[i]->parent = ap;
}
}
// 生成含信息(BT,x,ap)的新的根结点BT, 原BT和ap为子树指针。
void NewRoot(BTree &BT, int x, BTree ap)
{
BTree p = NULL;
p = new BTNode;
p->keynum = 1;
p->parent = NULL;
p->ptr[0] = BT;
p->ptr[1] = ap;
p->key[1] = x;
if (p->ptr[0])
p->ptr[0]->parent = p;
if (p->ptr[1])
p->ptr[1]->parent = p;
BT = p;
}
// 从B树中删除关键字key
int DeleteKey(BTree &BT, KeyType key)
{
Result R;
R = SearchBTree(BT, key);
if (R.tag == 1)
{
DeleteBTree(BT, R.pt, R.i);
return 1;
}
else
return 0;
}
// 从B树中删除结点q中第i个关键字
int DeleteBTree(BTree &BT, BTree q, int i)
{
Result R = {q, i, 1};
if (i < 1 || i > q->keynum)
return 0;
if (q->ptr[i])
SearchMinKey(q->ptr[i], R);
q->key[i] = R.pt->key[R.i];
Delete(BT, R.pt, R.i);
return 1;
}
// 从B树中删除结点q中第i个关键字(特殊)
void Delete(BTree &BT, BTree q, int i)
{
int s, tag, order;
BTree p, lc, rc;
s = ceil((double)M/2);
p = NULL; //q的父结点
order = -1; //q为p的第order个关键字
tag = 0;
if (!FoundParent(q, p, order)) // q是p的第order个孩子
tag = 1;
else
{
if (q->keynum >= s)
tag = 2;
else
{
if (tag == 0 && order < p->keynum && p->ptr[order+1]->keynum >= s)
tag = 3; // 右兄弟关键字个数 >= ceil(M/2)
if (tag == 0 && order > 0 && p->ptr[order-1]->keynum >= s)
tag = 4;
if (tag == 0 && order < p->keynum && p->ptr[order+1]->keynum == s-1)
tag = 5; // 右兄弟关键字个数 = ceil(M/2) - 1
if (tag == 0 && order > 0 && p->ptr[order-1]->keynum == s-1)
tag = 6;
}
}
switch(tag)
{
case 1:
if (q->keynum == 1 && i == 1)
{
BT = q->ptr[0];
BT->parent = NULL;
delete q;
q = NULL;
}
else
{
LeftMove(q, i+1, q, i, q->keynum - i);
q->keynum--;
}
break;
case 2:
LeftMove(q, i+1, q, i, q->keynum - i);
q->keynum--;
break;
case 3:
rc = p->ptr[order + 1];
LeftMove(q, i+1, q, i, q->keynum - i);
q->key[q->keynum] = p->key[order+1];
q->ptr[q->keynum] = rc->ptr[0];
p->key[order+1] = rc->key[1];
rc->ptr[0] = rc->ptr[1];
LeftMove(rc, 2, rc, 1, rc->keynum-1);
rc->keynum--;
break;
case 4:
lc = p->ptr[order - 1];
q->ptr[i] = q->ptr[i-1];
RightMove(q, i-1, q, i, i-1);
q->key[1] = p->key[order];
q->ptr[0] = lc->ptr[lc->keynum];
p->key[order] = lc->key[lc->keynum];
lc->keynum--;
break;
case 5:
rc = p->ptr[order + 1];
LeftMove(q, i+1, q, i, q->keynum - i);
q->key[q->keynum] = p->key[order+1];
q->ptr[q->keynum] = rc->ptr[0];
LeftMove(rc, 1, q, q->keynum + 1, rc->keynum);
q->keynum += rc->keynum;
for (int k=0;k<=rc->keynum;k++){
if (rc->ptr[k]){
rc->ptr[k]->parent = p->ptr[order];
}
}
delete (p->ptr[order+1]);
LeftMove(p, order + 2, p, order + 1, p->keynum - order - 1);
p->keynum--;
if (p->keynum < s - 1)
{
p->keynum++;
q = p;
Delete(BT, q, q->keynum);
}
break;
case 6:
lc = p->ptr[order-1]; //左兄弟结点
lc->key[lc->keynum+1] = p->key[order]; //双亲结点值下移到左兄弟结点
lc->ptr[lc->keynum+1] = q->ptr[0];
LeftMove(q,1,lc,lc->keynum+2,i-1);
LeftMove(q,i+1,lc,lc->keynum+i+1,q->keynum-i); //将q中删除结点的剩余结点全部拷贝到左兄弟中
lc->keynum += q->keynum;
for (int k=0;k<=q->keynum;k++){
if (q->ptr[k]){
q->ptr[k]->parent = p->ptr[order-1];
}
}
delete (p->ptr[order]); //释放q结点
LeftMove(p,order+1,p,order,p->keynum-order); //双亲结点中删除下移过后的order
p->keynum--;
if (p->keynum<s-1){
//如果双亲结点现有个数小于最小值则引起连锁反应
p->keynum++;
q = p;
Delete(BT,q,q->keynum); //删除不存在的结点
}
break;
}
}
// 找出BT中最小的关键字
int SearchMinKey(BTree BT, Result &R)
{
BTree q = BT;
while (q && q->ptr[0])
q = q->ptr[0];
if (q)
{
R.pt = q;
R.tag = 1;
R.i = 1;
return 1;
}
return 0;
}
// 找出双亲结点: q为p的第order个孩子
int FoundParent(BTree q, BTree &p, int &order)
{
p = q->parent;
if (p == NULL)
{
order = -1;
return 0;
}
else
{
for (order = 0; p->ptr[order] != q; order++);
return 1;
}
}
// 向左移动关键字和指针
int LeftMove(BTree old_ptr, int m, BTree new_ptr, int n, int len)
{
int k;
if (!old_ptr || !new_ptr || m < 1 || m > old_ptr->keynum )
return 0;
for (k = 0; k < len; k++, m++, n++)
{
new_ptr->key[n] = old_ptr->key[m];
new_ptr->ptr[n] = old_ptr->ptr[m];
}
return 1;
}
// 向右移动关键字和指针
int RightMove(BTree old_ptr, int m, BTree new_ptr, int n, int len)
{
int k;
if (!old_ptr || !new_ptr || m < 1 || m > old_ptr->keynum )
return 0;
for (k = 0; k < len; k++, m--, n--)
{
new_ptr->key[n] = old_ptr->key[m];
new_ptr->ptr[n - 1] = old_ptr->ptr[m - 1];
}
return 1;
}
// 层序输出B树
/*
void PrintBT_Level(BTree BT)
{
queue<BTNode*> temp;
temp.push(BT);
while (!temp.empty())
{
BTree T = temp.front();
temp.pop();
cout << "<";
for (int i = 0; i < T->keynum; i++)
{
if (T->ptr[i])
temp.push(T->ptr[i]);
cout << " " << T->key[i+1];
}
cout << ">";
if (T->ptr[T->keynum])
temp.push(T->ptr[T->keynum]);
cout << endl;
}
}
*/
void PrintBT_Level(BTree BT)
{
BTree p[100], q[100];
int i, j, k;
int a, b, count;
a = 1;
p[a] = BT;
count = 0;
while(a)
{
printf(" 第 %2d 行关键字:", ++count);
b = 0;
for(i=1; i<=a; i++)
{
printf("(");
for(j=0; j<=p[i]->keynum; j++)
{
if(j)
printf(" %2d", p[i]->key[j]);
if(p[i]->ptr[j])
q[++b] = p[i]->ptr[j];
}
printf(") ");
}
printf("\n");
a = b;
for(k=1; k<=b; k++)
p[k] = q[k];
}
}
// 中序输出B树
void PrintBT_InOrder(BTree BT)
{
if (!BT)
return;
for (int i = 0; i < BT->keynum; i++)
{
PrintBT_InOrder(BT->ptr[i]);
cout <<"<" << BT->key[i+1] << ">" << endl;
}
PrintBT_InOrder(BT->ptr[BT->keynum]);
}
- main.cpp
//
// main.cpp
// BTree
//
// Created by peiyu wang on 2019/3/25.
// Copyright © 2019 peiyu wang. All rights reserved.
//
#include "BTree.hpp"
int main(int argc, const char * argv[]) {
BTree bt = NULL;
CreateBTree(bt);
//PrintBT_InOrder(bt);
PrintBT_Level(bt);
DeleteKey(bt, 45);
PrintBT_Level(bt);
return 0;
}