#include <iostream>
using namespace std;
const int m = 3;//设定B树的阶数
//关键字的个数n必须满足:[m/2]-1≤n≤m-1
const int MaxKeyNum = m - 1;//最大关键字数量
const int MinKeyNum = m / 2 - 1;//最小关键字数量
typedef int ElemType;//关键字类型定义
typedef struct BTNode {
int keynum; //结点中关键字的个数
ElemType key[m + 1]; //关键字数组,0号单元未用
struct BTNode* parent;//双亲结点指针
struct BTNode* ptr[m + 1]; //子树结点指针数组
}BTNode, * BTree;
typedef struct {
BTNode* pt; //指向找到的结点
int i; //在结点中的关键字位置
int tag; //查找是否成功的标志
}Result; //查找结果类型
//队列---------------------------------------
typedef BTNode* QElemType;
typedef struct QNode {
QElemType data;
struct QNode* next;
}QNode, * QueuePtr;
typedef struct {
QueuePtr front;
QueuePtr rear;
}LinkQueue;
bool InitQueue(LinkQueue& Q);//初始化链队列
bool EnQueue(LinkQueue& Q, QElemType e);//将元素e入队
bool DeQueue(LinkQueue& Q, QElemType& e);//将队首元素出队,并用元素e返回
bool DestroyQueue(LinkQueue& Q);//销毁队列
int QueueLength(LinkQueue Q);//返回队列长度
bool InitQueue(LinkQueue& Q) {
Q.front = Q.rear = new QNode;//头节点
if (!Q.front || !Q.rear)return 0;
Q.front->next = NULL;
return 1;
}
bool EnQueue(LinkQueue& Q, QElemType e) {
QueuePtr temp = new QNode;
if (!temp)return 0;
temp->data = e;
temp->next = NULL;
Q.rear->next = temp;
Q.rear = temp;
return 1;
}
bool DeQueue(LinkQueue& Q, QElemType& e) {
if (Q.front == Q.rear)
return 0;
QueuePtr temp = Q.front->next;//指向第一个结点的指针
e = temp->data;
Q.front->next = temp->next;
if (Q.rear == temp)//若出队的是尾指针指向结点,则需要调整尾指针,否则删除后尾指针指向空
Q.rear = Q.front;
delete temp;
return 1;
}
bool DestroyQueue(LinkQueue& Q) {
QueuePtr temp = Q.front;
while (Q.front) {
Q.front = Q.front->next;
delete temp;
temp = Q.front;
}
return 1;
}
int QueueLength(LinkQueue Q) {
int n = 0;
while (Q.front != Q.rear) {
n++;
Q.front = Q.front->next;
}
return n;
}
//B树---------------------------------------
void CreateBTree(BTree& T);//创建B树
void InsertBTNode(BTNode*& p, int i, ElemType e, BTNode* q);//将关键字e和结点q分别插入到p->key[i+1]和p->ptr[i+1]中
void InsertBTree(BTree& T, int i, ElemType e, BTNode* p);//将关键字e和新建结点去q分别插入到p->key[i+1]和p->ptr[i+1]中,加入分裂机制
int SearchBTNode(BTNode* p, ElemType e);//返回结点p中元素e的应插入位置
void SearchBTree(BTree T, ElemType e, Result& s);//在B树中查找元素e,将查找结果用s返回,
void CreateNewNode(BTNode*& T, ElemType e, BTNode* p, BTNode* q);//p和q是子树指针,因为m-1等于2,所以有两个
void SplitBTNode(BTNode*& p, BTNode*& q);//将结点p分裂成两个结点,前一半保留,后一半移入结点q,最中间位置的点升为双亲结点
void Traverse(BTree T, LinkQueue L, int newline = 0, int sum = 0);//遍历
bool DestroyBTree(BTree& T);//销毁B树
int FindBTNode(BTNode* p, ElemType e, int& i);//判断元素e是否存在于结点p中,用i返回位次
void Substitution(BTNode* p, int i);//查找被删关键字p->key[i](在非叶子结点中)的替代叶子结点(右子树中值最小的关键字)
void Remove(BTNode* p, int i);//删除p结点的key[i+1]和ptr[i]
void AdjustBTree(BTNode* p, int i);//删除之后需要调整B树
void MoveRight(BTNode* p, int i);
void MoveLeft(BTNode* p, int i);
void Combine(BTNode* p, int i);
int BTreeDelete(BTree p, ElemType e);//删除B树中的元素e,删除失败返回0
void CreateBTree(BTree& T) {
Result s;
s.i = 0; s.pt = NULL; s.tag = 0;
cout << "即将建立" << m << "阶B树:" << endl;
T = NULL;//初始化B树为空
cout << "输入关键字个数:" << endl;
int n; cin >> n;
cout << "输入结点关键字值:" << endl;
ElemType e;
for (int i = 0; i < n; i++) {
cin >> e;
SearchBTree(T, e, s);
if (s.tag == 0) //证明元素e未在B树中,可以插入
InsertBTree(T, s.i, e, s.pt);
}
}
int SearchBTNode(BTNode* p, ElemType e) {
int num = 0;
while (num < p->keynum && p->key[num + 1] <= e)
num++;//循环结束后第num+1位大于e,第num位小于等于e
return num;
}
void SearchBTree(BTree T, ElemType e, Result& s) {
bool tag = 0;//标记是否找到元素e
int i = 0;//用来接收元素的应插入位次
BTNode* p = T, * q = NULL;//初始化结点p指向正在查询的结点,结点q指向p的双亲
while (p != NULL && tag != 1) {//除非查到底至空,或者已经查到,否则一直循环下去
i = SearchBTNode(p, e);
if (p->key[i] == e && i > 0)//key的首地址不使用,因此i是从1开始的
tag = 1;//找到
else {
q = p;
p = p->ptr[i];//应插入位置非所查元素就继续按此路线查下去
}
}
if (tag == 1) {//查找成功,返回当前结点
s.pt = p;
s.i = i;
s.tag = 1;
}
else {//查找失败,返回最后查到的结点
s.pt = q;
s.i = i;
s.tag = 0;
}
}
void InsertBTNode(BTNode*& p, int i, ElemType e, BTNode* q) {
int j;
for (j = p->keynum; j > i; j--) {//整体后移空出一个位置
p->key[j + 1] = p->key[j];
p->ptr[j + 1] = p->ptr[j];
}
p->key[i + 1] = e;
p->ptr[i + 1] = q;
if (q != NULL)
q->parent = p;
p->keynum++;
}
void InsertBTree(BTree& T, int i, ElemType e, BTNode* p) {
BTNode* q;
bool finish_tag = 0, newnode_tag = 0;//标志是否插入完成,标志是否需要新结点
if (p == NULL)//若传进来的应插入结点为空,则证明T是空树,直接插入根节点
CreateNewNode(T, e, NULL, NULL);//子树为空
else {
q = NULL;
while (finish_tag == 0 && newnode_tag == 0) {//不需要新结点,查找没有结束
InsertBTNode(p, i, e, q);//将关键字e和结点q分别插入到p->key[i+1]和p->ptr[i+1]
if (p->keynum <= MaxKeyNum)
finish_tag = 1;//添加结点后并未超出范围
else {//添加结点后超出范围
int s = (m + 1) / 2;//中间位置
SplitBTNode(p, q);//分裂结点
e = p->key[s];//废物利用,让e负责接收中间位置的关键值
if (p->parent) {//查找接收中间值后的e的插入位置
p = p->parent;
i = SearchBTNode(p, e);
}
else//没找到e,需要新结点
newnode_tag = 1;
}
}
if (newnode_tag == 1)//根结点已分裂为结点p和q
CreateNewNode(T, e, p, q);//生成新根结点t,p和q为子树指针
}
}
void SplitBTNode(BTNode*& p, BTNode*& q) {
//将结点p分裂成两个结点,前一半保留,后一半移入结点q
int i;
int s = (m + 1) / 2;//中间位置
q = new BTNode;//给结点q分配空间
q->ptr[0] = p->ptr[s];//后一半移入结点q
for (i = s + 1; i <= m; i++) {
q->key[i - s] = p->key[i];//key是从1开始计算的
q->ptr[i - s] = p->ptr[i];
}
q->keynum = p->keynum - s;
q->parent = p->parent;//分裂后还是兄弟结点
for (i = 0; i <= q->keynum; i++)//修改双亲指针,从p改为q
if (q->ptr[i] != NULL)
q->ptr[i]->parent = q;
p->keynum = s - 1;//结点p的前一半保留,中间位置的s升级为双亲结点
}
void CreateNewNode(BTNode*& T, ElemType e, BTNode* p, BTNode* q) {
T = new BTNode;//分配空间
T->keynum = 1;
T->ptr[0] = p;
T->ptr[1] = q;
T->key[1] = e;
if (p != NULL)//若子树结点为空,则不需要调整子树的双亲结点
p->parent = T;
if (q != NULL)
q->parent = T;
T->parent = NULL;
}
void Traverse(BTree T, LinkQueue L, int newline, int sum) {
BTree p;
if (T != NULL) {
cout << " [ ";
EnQueue(L, T->ptr[0]);//入队
for (int i = 1; i <= T->keynum; i++) {
cout << " " << T->key[i] << " ";
EnQueue(L, T->ptr[i]);//子结点入队
}
sum += T->keynum + 1;//keynum个关键字有keynum+1个空当存指针
cout << "]";
if (newline == 0) {//需要另起一行
cout << endl;
newline = sum - 1;
sum = 0;
}
else
newline--;
}
if (QueueLength(L) != 0) {//l不为空
DeQueue(L, p);//出队,以p返回
Traverse(p, L, newline, sum); //遍历出队结点
}
}
bool DestroyBTree(BTree& T) {
if (!T) { //B树不为空
for (int i = 0; i <= T->keynum; i++)//递归释放每一个结点
DestroyBTree(T->ptr[i]);
delete T;
}
T = NULL;
return 1;
}
int FindBTNode(BTNode* p, ElemType e, int& i) {
if(e < p->key[1]) {//结点p中查找关键字k失败
i = 0;
return 0;
}
else {//在p结点中查找
i = p->keynum;
while (e < p->key[i] && i>1)
i--;
if (e == p->key[i])//结点p中查找关键字k成功
return 1;
}
return 0;
}
void Substitution(BTNode* p, int i) {
//查找被删关键字p->key[i](在非叶子结点中)的替代叶子结点(右子树中值最小的关键字)
BTNode* q;
for (q = p->ptr[i]; q->ptr[0] != NULL; q = q->ptr[0]);//左边小
p->key[i] = q->key[1]; //复制关键字值
}
void Remove(BTNode* p, int i) {
//从p结点删除key[i]和它的孩子指针ptr[i]
int j;
for (j = i + 1; j <= p->keynum; j++) {//前移删除key[i]和ptr[i],ptr[0]是最左侧不用删,必须留着!!!!
p->key[j - 1] = p->key[j];
p->ptr[j - 1] = p->ptr[j];
}
p->keynum--;
}
void MoveRight(BTNode* p, int i) {
/*将双亲结点p中的最后i个关键字移入右结点last_q中
将左结点first_q中的最后i个关键字移入双亲结点p中*/
BTNode* last_q = p->ptr[i];
BTNode* first_q = p->ptr[i - 1];
for (int j = last_q->keynum; j > 0; j--) {//将右兄弟last_q中所有关键字向后移动一位
last_q->key[j + 1] = first_q->key[j];
last_q->ptr[j + 1] = first_q->ptr[j];
}
last_q->ptr[1] = last_q->ptr[0];//从双亲结点p移动关键字到右兄弟last_q中,原来的最左侧孩子现在是第二个,现在的最左侧是p带过来的
last_q->key[1] = p->key[i];
last_q->keynum++;
p->key[i] = first_q->key[first_q->keynum];//将左兄弟first_q中最后一个关键字移动到双亲结点p中
p->ptr[i]->ptr[0] = first_q->ptr[first_q->keynum];//这个是first_q最后一个关键字左边的孩子结点,带到了p中
first_q->keynum--;
}
void MoveLeft(BTNode* p, int i) {
/*将双亲结点p中的第i个关键字移入左结点first_q中,
将右结点q中的第i个关键字移入双亲结点p中*/
BTNode* first_q = p->ptr[i - 1];
BTNode* last_q = p->ptr[i];
first_q->keynum++;//把双亲结点p中的关键字移动到左兄弟first_q中
first_q->key[first_q->keynum] = p->key[i];
first_q->ptr[first_q->keynum] = p->ptr[i]->ptr[0];
p->key[i] = last_q->key[1];//把右兄弟last_q中的关键字移动到双亲节点p中
last_q->ptr[0] = last_q->ptr[1];
last_q->keynum--;
for (int j = 1; j <= last_q->keynum; j++) {//将右兄弟last_q中所有关键字向前移动一位
last_q->key[j] = last_q->key[j + 1];
last_q->ptr[j] = last_q->ptr[j + 1];
}
}
void Combine(BTNode* p, int i) {
/*将双亲结点p、右结点q合并入左结点aq,
并调整双亲结点p中的剩余关键字的位置*/
int j;
BTNode* q = p->ptr[i];
BTNode* aq = p->ptr[i - 1];
aq->keynum++;//将双亲结点的关键字p->key[i]插入到左结点aq
aq->key[aq->keynum] = p->key[i];
aq->ptr[aq->keynum] = q->ptr[0];
for (j = 1; j <= q->keynum; j++) {//将右结点q中的所有关键字插入到左结点aq
aq->keynum++;
aq->key[aq->keynum] = q->key[j];
aq->ptr[aq->keynum] = q->ptr[j];
}
for (j = i; j < p->keynum; j++) {//将双亲结点p中的p->key[i]后的所有关键字向前移动一位
p->key[j] = p->key[j + 1];
p->ptr[j] = p->ptr[j + 1];
}
p->keynum--;//修改双亲结点p的keynum值
delete q;//释放空右结点q的空间
}
void AdjustBTree(BTNode* p, int i) {
//删除结点p中的第i个关键字后,调整B树
if (i == 0) //删除的是最左边关键字
if (p->ptr[1]->keynum > MinKeyNum) //右结点可以借
MoveLeft(p, 1);
else //右兄弟不够借
Combine(p, 1);
else if (i == p->keynum) //删除的是最右边关键字
if (p->ptr[i - 1]->keynum > MinKeyNum) //左结点可以借
MoveRight(p, i);
else //左结点不够借
Combine(p, i);
else if (p->ptr[i - 1]->keynum > MinKeyNum) //删除关键字在中部且左结点够借
MoveRight(p, i);
else if (p->ptr[i + 1]->keynum > MinKeyNum) //删除关键字在中部且右结点够借
MoveLeft(p, i + 1);
else //删除关键字在中部且左右结点都不够借
Combine(p, i);
}
int BTreeDelete(BTNode* p, ElemType e) {
int i;
int found_tag;//查找标志
if (p == NULL)
return 0;
else {
found_tag = FindBTNode(p, e, i);//返回查找结果
if (found_tag == 1) {//查找成功 ,i从1开始
if (p->ptr[i - 1] != NULL) {//删除的是非叶子结点
Substitution(p, i);//寻找相邻关键字(右子树中最小的关键字)
BTreeDelete(p->ptr[i], p->key[i]);//执行删除操作
}
else
Remove(p, i);//叶子结点直接删,不用考虑找代替
}
else
found_tag = BTreeDelete(p->ptr[i], e);//沿孩子结点递归查找并删除关键字e
if (p->ptr[i] != NULL)
if (p->ptr[i]->keynum < MinKeyNum)//删除后关键字个数小于MIN
AdjustBTree(p, i);//调整B树
return found_tag;
}
}
int main()
{
Result s;
s.i = 0; s.pt = NULL; s.tag = 0;
BTree T; LinkQueue L; InitQueue(L);
CreateBTree(T);
cout << "请输入要查找的关键值:" << endl;
ElemType e;
cin >> e;
SearchBTree(T, e, s);
if (s.tag == 1)
cout << e << "找到了!" << endl;
else
cout << "没找到!" << endl;
Traverse(T, L);
cout << "请输入要删除的元素:" << endl;
ElemType ee;
cin >> ee;
if (!BTreeDelete(T, ee))
cout << "删除元素不存在!" << endl;
else {
cout << "删除后遍历:" << endl;
Traverse(T, L);
}
if (DestroyBTree(T))
cout << "销毁成功!" << endl;
}
实验样例