习题内容:
1.掌握B树的存贮结构。
2.实现B树中关键字值的插入及删除操作。
B树的定义:
B树也称B-树,它是一颗多路平衡查找树。我们描述一颗B树时需要指定它的阶数,阶数表示了一个结点最多有多少个孩子结点,一般用字母M表示阶数。当M取2时,就是我们常见的二叉搜索树。
一颗M阶的B树定义如下:
1)每个结点最多有M-1个关键字。
2)根结点最少可以只有1个关键字。
3)非根结点至少有Math.ceil(M / 2)-1个关键字。
4)每个结点中的关键字都按照从小到大的顺序排列,每个关键字的左子树中的所有关键字都小于它,而右子树中的所有关键字都大于它。
扫描二维码关注公众号,回复:
4631673 查看本文章
5)所有叶子结点都位于同一层,或者说根结点到每个叶子结点的长度都相同。
代码实现:
/*测试环境:VS2017*/
#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef struct _BTreeNode
{
int key_count;
int* key;
bool leaf;
_BTreeNode** child;
}BTreeNode, *PBTreeNode;
class BTree
{
private:
PBTreeNode root;
int t;//B树的度,树的阶数为2t***m=2t,t=m/2
public:
BTree(int m):root(NULL), t(m){}//构造
~BTree(){DeleteTree(root);delete root;}//析构
//基本操作:插入,删除,打印,查询
void Insert(int key);
void Delete(int key) { DeleteNonHalf(root, key); }
void Display() { Print(root); }
bool Search(int key);
private:
PBTreeNode SearchBT(PBTreeNode pNode, int key);
PBTreeNode AllocateNode();//划分节点,初始化
PBTreeNode UnionChild(PBTreeNode pParent, PBTreeNode pCLeft, PBTreeNode pCRight, int index);//结合兄弟节点
int Max(PBTreeNode pNode);
int Min(PBTreeNode pNode);
void DeleteNonHalf(PBTreeNode pNode, int key);//删除不足的节点
void InsertNonfull(PBTreeNode pNode, int key);//插入未满的节点
void SplitChild(PBTreeNode pParent, int index, PBTreeNode pChild);//划分子节点
void DeallocateNode(PBTreeNode pNode);//解除划分
void DeleteTree(PBTreeNode pNode);//删除树
void Print(PBTreeNode pNode);//打印树
};
void BTree::Insert(int key)
{
PBTreeNode r = root;
if (r == NULL)
{
r = AllocateNode();
r->leaf = true;
r->key_count = 0;
root = r;
}
if (r != NULL && r->key_count == (2 * t - 1))
{
PBTreeNode s = AllocateNode();
root = s;
s->leaf = false;
s->key_count = 0;
s->child[1] = r;
SplitChild(s, 1, r);
InsertNonfull(s, key);
}
else
{
InsertNonfull(r, key);
}
cout << "成功插入关键字:" << key << endl;
}
bool BTree::Search(int key)
{
if (SearchBT(root, key))
return false;
else
return true;
}
PBTreeNode BTree::AllocateNode()
{
PBTreeNode pTemp = new BTreeNode;
pTemp->key = new int[2 * t];
pTemp->child = new PBTreeNode[2 * t + 1];
//初始化
for (int i = 0; i < 2 * t; i++)
{
pTemp->key[i] = 0;
pTemp->child[i] = NULL;
}
pTemp->child[2 * t] = NULL;
return pTemp;
}
PBTreeNode BTree::SearchBT(PBTreeNode pNode, int key)
{
int i = 1;
while (i <= pNode->key_count && key > pNode->key[i])
i++;
if (i < pNode->key_count && key == pNode->key[i])
return pNode->child[i];
if (pNode->leaf)
return NULL;
else
return SearchBT(pNode->child[i], key);
}
PBTreeNode BTree::UnionChild(PBTreeNode pParent, PBTreeNode pCLeft, PBTreeNode pCRight, int index)
{
for (int i = 1; i < t; i++)
pCLeft->key[t + i] = pCRight->key[i];
pCLeft->key[t] = pParent->key[index];
for (int i = 1; i <= t; i++)
pCLeft->child[t + i] = pCRight->child[i];
pCLeft->key_count = 2 * t - 1;
for (int i = index; i < pParent->key_count; i++)
pParent->key[i] = pParent->key[i + 1];
for (int i = index + 1; i <= pParent->key_count; i++)
pParent->child[i] = pParent->child[i + 1];
pParent->key_count--;
DeallocateNode(pCRight);
if (pParent->key_count == 0)
{
DeallocateNode(root);
root = pCLeft;
}
return pCLeft;
}
int BTree::Max(PBTreeNode pNode)
{
while (!pNode->leaf)
{
pNode = pNode->child[pNode->key_count + 1];
}
return pNode->key[pNode->key_count];
}
int BTree::Min(PBTreeNode pNode)
{
while (!pNode->leaf)
{
pNode = pNode->child[1];
}
return pNode->key[1];
}
void BTree::DeleteNonHalf(PBTreeNode pNode, int key)
{
int i = 1;
while (i <= pNode->key_count && key > pNode->key[i])
i++;
if (pNode->leaf)
{
if (i <= pNode->key_count && key == pNode->key[i])
{
for (int j = i; j < pNode->key_count; j++)
pNode->key[j] = pNode->key[j + 1];
pNode->key_count--;
cout << "成功删除关键字:" << key << endl;
return;
}
else
{
//cout << "未能找到关键字:" << key << endl;
return;
}
}
if (i <= pNode->key_count && key == pNode->key[i])
{
if (pNode->child[i]->key_count >= t)
{
key = Max(pNode->child[i]);
pNode->key[i] = key;
DeleteNonHalf(pNode->child[i], key);
}
else if (pNode->child[i + 1]->key_count >= t)
{
key = Min(pNode->child[i + 1]);
pNode->key[i] = key;
DeleteNonHalf(pNode->child[i + 1], key);
}
else
{
PBTreeNode pChild = UnionChild(pNode, pNode->child[i], pNode->child[i + 1], i);
DeleteNonHalf(pChild, key);
}
}
else if (pNode->child[i]->key_count == t - 1)
{
if (i > 1 && pNode->child[i - 1]->key_count >= t)
{
PBTreeNode pMidNode = pNode->child[i];
PBTreeNode pPreNode = pNode->child[i - 1];
int nPreNodeKeyCount = pPreNode->key_count;
for (int j = pMidNode->key_count + 1; j > 1; j--)
pMidNode->key[j] = pMidNode->key[j - 1];
pMidNode->key[1] = pNode->key[i - 1];
for (int j = pMidNode->key_count + 2; j > 1; j--)
pMidNode->child[j] = pMidNode->child[j - 1];
pMidNode->child[1] = pPreNode->child[nPreNodeKeyCount + 1];
pMidNode->key_count++;
pNode->key[i - 1] = pPreNode->key[nPreNodeKeyCount];
pPreNode->key[nPreNodeKeyCount] = 0;
pPreNode->key[nPreNodeKeyCount + 1] = NULL;
pPreNode->key_count--;
DeleteNonHalf(pMidNode, key);
}
else if (i <= pNode->key_count && pNode->child[i + 1]->key_count >= t)
{
PBTreeNode pMidNode = pNode->child[i];
PBTreeNode pNextNode = pNode->child[i + 1];
int nNextNodeKeyCount = pNextNode->key_count;
int nMidNodeKeyCount = pMidNode->key_count;
pMidNode->key[nMidNodeKeyCount + 1] = pNode->key[i];
pMidNode->child[nMidNodeKeyCount + 2] = pNextNode->child[1];
pMidNode->key_count++;
pNode->key[i] = pNextNode->key[1];
for (int j = 1; j < nNextNodeKeyCount; j++)
pNextNode->key[j] = pNextNode->key[j + 1];
for (int j = 1; j <= nNextNodeKeyCount; j++)
pNextNode->child[j] = pNextNode->child[j + 1];
pNextNode->key_count--;
DeleteNonHalf(pMidNode, key);
}
else
{
if (i > pNode->key_count)//当i指向最后一个关键字时,合并时往前移动一步
i--;
PBTreeNode pChild = UnionChild(pNode, pNode->child[i], pNode->child[i + 1], i);
DeleteNonHalf(pChild, key);
}
}
DeleteNonHalf(pNode->child[i], key);
}
void BTree::DeallocateNode(PBTreeNode pNode)
{
delete[] pNode->key;
delete[] pNode->child;
delete pNode;
}
void BTree::SplitChild(PBTreeNode pParent, int index, PBTreeNode pChild)
{
PBTreeNode pChildEx = AllocateNode();
pChildEx->leaf = pChild->leaf;
pChildEx->key_count = t - 1;
for (int j = 1; j < t; j++)
pChildEx->key[j] = pChild->key[j + t];
if (!pChild->leaf)
for (int j = 1; j <= t; j++)
pChildEx->child[j] = pChild->child[j + t];
pChild->key_count = t - 1;
for (int j = pParent->key_count + 1; j > index; j--)
pParent->child[j + 1] = pParent->child[j];
pParent->child[index + 1] = pChildEx;
for (int j = pParent->key_count; j >= index; j--)
pParent->key[j + 1] = pParent->key[j];
pParent->key[index] = pChild->key[t];
pParent->key_count++;
}
void BTree::InsertNonfull(PBTreeNode pNode, int key)
{
int i = pNode->key_count;
if (pNode->leaf)
{
while (i >= 1 && key < pNode->key[i])
{
pNode->key[i + 1] = pNode->key[i];
i--;
}
pNode->key[i + 1] = key;
pNode->key_count++;
}
else
{
while (i >= 1 && key < pNode->key[i])
i--;
i++;
if (pNode->child[i]->key_count == (2*t - 1))
{
SplitChild(pNode, i, pNode->child[i]);
if (key > pNode->key[i])
i++;
}
InsertNonfull(pNode->child[i], key);
}
}
void BTree::DeleteTree(PBTreeNode pNode)//最后的根元素没有被删除,但是根里的关键字数组和孩子指针数组已经删除
{
if (pNode->leaf)
{
delete[] pNode->key;
delete[] pNode->child;
}
else
{
for (int i = 1; i <= pNode->key_count + 1; i++)
{
DeleteTree(pNode->child[i]);
delete pNode->child[i];
}
delete[] pNode->key;
delete[] pNode->child;
}
}
void BTree::Print(PBTreeNode pNode)
{
if (pNode->leaf)
{
cout << "叶子节点数目及成员:" << pNode->key_count << " ";
for (int i = 1; i <= pNode->key_count; i++)
{
cout << pNode->key[i] << " ";
}
cout << endl;
}
else
{
for (int i = 1; i <= pNode->key_count + 1; i++)
Print(pNode->child[i]);
cout << "内部节点数目及成员:" << pNode->key_count << " ";
for (int i = 1; i <= pNode->key_count; i++)
{
cout << pNode->key[i] << " ";
}
cout << endl;
}
}
int main()
{
int num, n,data;
cout << "请分别输入关键字的个数及要建立的B树的度数(中间用空格隔开):";
cin >> num >> n;
BTree BT(n);
cout << "请输入" << num << "个建立B树所需的关键字(中间用空格隔开):";
for (int i = num; i > 0; i--)
{
cin >> data;
BT.Insert(data);
}
cout << "B树创建成功!" << endl;
BT.Display();
cout << "****************************************************************************" << endl;
cin.ignore(1000, '\n');
char s,ss[100];
while (1)
{
cout << "请输入操作(I:插入,D:删除;以 ‘#’结束;多个数据间用空格隔开):" << endl;
cin >> s;
if (s == 'I')
{
while (cin >> ss && ss[0] != '#')
{
data = atoi(ss);
/*if (BT.Search(data))
{
cout << "关键字" << data << "已存在!" << endl;
continue;
}*/
BT.Insert(data);
}
BT.Display();
cout << "****************************************************************************" << endl;
}
else if (s == 'D')
{
while (cin >> ss && ss[0] != '#')
{
data = atoi(ss);
/*if (BT.Search(data)==false)
{
cout << "未能找到关键字:" << data << endl;
continue;
}*/
BT.Delete(data);
//cout << "成功删除关键字:" << data << endl;
}
BT.Display();
cout << "****************************************************************************" << endl;
}
else
cout << "输入错误!" << endl;
}
return 0;
}
/*
test data :
10 2
9 4 6 10 22 8 1 2 3 5 7 11 33 44 55 66 77 88 99
I 101 1001 111 1010 33 #
D 8 33 10 #
*/