最近在复习数据结构,为了加深对B树的理解,整理了如下笔记,然后用C语言实现了一下B树;很久没有使用C语言了,代码有些地方不符合规范或者本文理解有问题,欢迎指正;
一、B树定义
B树,又称多路平衡查找树,B树中所有结点孩子个数的最大值为B树的阶。m阶B树定义如下:空树或者满足如下特征的m叉树:
1)树中每个结点至多有m棵子树,即至多有m-1个关键字;
2)若根结点不是终端结点,则至少有2棵子树,即至少一个关键字;
3)除根节点外所有非叶结点至少有
⌈
m
/
2
⌉
\lceil m/2 \rceil
⌈m/2⌉棵子树,即至少有
⌈
m
/
2
⌉
\lceil m/2 \rceil
⌈m/2⌉-1个关键字;
4)所有非叶结点结构如下:
其中,Ki(i=0,1,2,…,n)为结点的关键字,且满足K1<K2<…<Kn;Pi为指向子树根结点的指针,且指针P i-1所指子树中所有结点关键字均小于K i,P i所指子树中所有结点关键字均大于K i,其中 ⌈ m / 2 ⌉ \lceil m/2 \rceil ⌈m/2⌉ - 1 ⩽ \leqslant ⩽ n ⩽ \leqslant ⩽m - 1,表示关键字的个数;
5)所有空结点出现在同一层次,这些空结点可以视为查找失败的情况;
![7阶B树](https://i-blog.csdnimg.cn/blog_migrate/69cb3c3bc10eb100d826c8881b9d9881.png)
二、B树的性质:
此处讨论的性质不把外部结点算作叶子结点;
1)非叶结点孩子个数等于关键字个数加1;
2)结点中关键字从左至右是递增的,关键字两侧均有指向子树的指针;
3)B树的高度:对于含n个关键字、高度h、阶数为m的B树
i)h
⩾
\geqslant
⩾logm(n+1)
B树每个结点最多有m棵子树,m-1个关键字,所以应满足n
⩽
\leqslant
⩽ (m-1)
(
1
+
m
+
m
2
+
m
3
+
.
.
.
+
m
h
−
1
)
=
m
h
−
1
;
(1+m+m^2+m^3+...+m^{h-1})=m^h-1;
(1+m+m2+m3+...+mh−1)=mh−1;
ii)h
⩽
\leqslant
⩽log
⌈
m
/
2
⌉
\lceil m/2 \rceil
⌈m/2⌉((n+1)/2)+1;
让每个结点中关键字数目个数达到最少,则B树高度达到最大;此时根结点有一个关键字,有两个子树,剩余所有结点均有
⌈
m
/
2
⌉
\lceil m/2 \rceil
⌈m/2⌉ - 1个关键字,则总关键字个数为1+2*(1+
⌈
m
/
2
⌉
\lceil m/2 \rceil
⌈m/2⌉+
⌈
m
/
2
⌉
\lceil m/2 \rceil
⌈m/2⌉^2+
⌈
m
/
2
⌉
\lceil m/2 \rceil
⌈m/2⌉^3+…+
⌈
m
/
2
⌉
h
−
2
)
∗
(
⌈
m
/
2
⌉
−
1
)
=
1
+
2
∗
(
⌈
m
/
2
⌉
h
−
1
−
1
\lceil m/2 \rceil^{h-2})*(\lceil m/2 \rceil-1)=1+2*(\lceil m/2 \rceil^{h-1} - 1
⌈m/2⌉h−2)∗(⌈m/2⌉−1)=1+2∗(⌈m/2⌉h−1−1) = 2*
⌈
m
/
2
⌉
h
−
1
\lceil m/2\rceil^{h-1}
⌈m/2⌉h−1 - 1,所以,n
⩾
\geqslant
⩾ 2*
⌈
m
/
2
⌉
h
−
1
\lceil m/2\rceil^{h-1}
⌈m/2⌉h−1 - 1;
三、B树的查找
与二叉树查找类似,每次先从根结点查找多关键字有序表,根据该节点查找结果做多路分支的决定;
B树查找步骤如下:
1)工作指针P指向根结点;
2)比较关键字key与结点P的关键字列表,如果有Ki=key,直接返回此节结点和位置i即可;否则,直到找到Ki
<
\lt
< key
<
\lt
<Ki + 1,令P = P->Pi;
3)重复上述步骤,直至找到或者P为NULL;若P=NULL,说明没找到;
四、B树的插入
B树的插入相比于二叉查找树要复杂的多,B树插入后可能会引起某个结点中关键字数目过多,不符合m阶B树的定义,这时会涉及到结点分裂的操作;将关键字插入B树过程如下:
1)定位:与B树的查找算法,找出插入位置(叶子结点);
2)在B树中,每个结点关键字个数在区间[
⌈
m
/
2
⌉
\lceil m/2 \rceil
⌈m/2⌉ - 1, m-1]之间,若结点关键字个数
⩽
\leqslant
⩽m-2,则插入关键字后仍然符合B树定义,直接插入即可;否则,要执行如下的结点分裂操作:
从中间位置(
⌈
m
/
2
⌉
\lceil m/2 \rceil
⌈m/2⌉)将插入关键字后的结点进行分裂,位置
⌈
m
/
2
⌉
\lceil m/2 \rceil
⌈m/2⌉处的关键字进位到双亲结点,[0-
⌈
m
/
2
⌉
\lceil m/2 \rceil
⌈m/2⌉-1]处的关键字保留在原结点,新生成一个结点存放[
⌈
m
/
2
⌉
\lceil m/2 \rceil
⌈m/2⌉+1,m-1]处的关键字;若进位后引起双亲结点不符合定义,则继续向上进位,直到根节点;如果进位到根节点引起根结点不符合定义,则重新创造一个根节点,只包含进位的关键字;
下面给出两种情况下插入关键字的操作步骤:
五、B树的删除
删除关键字后也可能导致结点不符合B树定义,此时会涉及到结点的合并操作;从B树删除关键字key的过程如下(设被删关键字所在结点为iNode,做兄弟结点为lNode,右兄弟结点为rNode,双亲结点为pNode):
情形1)iNode不是叶子结点时:用key的前驱或后继key’来代替k,然后删除key’,此时key’必定落在叶结点,则转换为了情形2;
情形2)iNode是叶子结点,分三种情况:
①iNode关键字个数 ⩾ ⌈ m / 2 ⌉ \geqslant \lceil m/2\rceil ⩾⌈m/2⌉,则表明删除key后仍满足B树定义,直接删除即可;
②兄弟够借。若iNode关键字个数 < ⌈ m / 2 ⌉ \lt \lceil m/2\rceil <⌈m/2⌉且lNode或rNode关键字个数 ⩾ ⌈ m / 2 ⌉ \geqslant \lceil m/2\rceil ⩾⌈m/2⌉,则向lNode借(假设lNode够借); 借用过程如下:
pNode的指针p i指向iNode,则用K i代替key,然后用lNode的最后一个关键字补上pNode的K i,若lNode的最后一个子结点不空,则将此子结点插入iNode;
③兄弟不够借。若iNode、lNode、rNode关键字个数均 < ⌈ m / 2 ⌉ \lt \lceil m/2\rceil <⌈m/2⌉,则将关键字删除后的iNode和lNode或者rNode合并;合并时,双亲结点关键字个数-1,若因此导致双亲结点pNode不符合B数定义(pNode为根节点时,pNode的关键字个数 > \gt > 1即可),则pNode要和其左右兄弟结点进行调整(借或合并),重复上述步骤,直至符合B树要求; 合并过程如下:
i)若pNode为根结点且关键字个数为1,则将pNode的关键字和子结点加上iNode剩余的信息并入rNode(假设rNode存在),并令rNode为B树根节点;
ii)若pNode不是根,pNode的指针pi指向iNode,则把pNode的关键字K i和iNode剩余信息并入rNode,然后删除iNode;
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/67754be5d0179bb20f693c7605360ca9.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/0536c875e7a789a6d52f3b25d61826d8.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/ebddc340b348b28df0ca7a55a38991cd.png)
六、C语言实现
项目结构如下:
1、BTree.h:
#pragma once
#define MAX_ORDER 6 // B树最大阶数
#define TREE_ORDER 4 // B数阶数
#define MIN_KEY_NUM (TREE_ORDER - 1)/2 // 非根结点最小关键字数目
#define MAX_KEY_NUM TREE_ORDER - 1 // 每个结点最大关键字数目
#define ROOT_MIN_KEY_NUM 1 // 根结点最大关键字数目
#define CEIL(x) (x+1)/2 // 向上取整
// 因为此B树每个结点数据是从下标0开始存储的,所以-1
#define ROOT_INDEX (TREE_ORDER - 1)/2 //结点分裂时需要进位的数据下标
#define NOT_FOUND -1 // 未找到标志
// 关键字类型
typedef int KeyType;
// 结点数据类型
typedef struct MyType {
KeyType key;
}DataType;
// BTree操作的状态
enum Status{
OK,
ERROR
};
// 查找状态标志
enum Tag{
SUCCESS, //查找成功
FAIL, // 查找失败
INSERT, // 查找插入位置
DELETE
};
// BlanceTree结点
typedef struct BTreeNode{
int keyNum;
int childNum;
BTreeNode *parent;
BTreeNode *child[TREE_ORDER];
DataType data[MAX_KEY_NUM];
}BTNode, *BTree;
// BTree查找结果
typedef struct {
BTNode *node; // 查找的结点
int pos; // 结点中关键字的位置
Tag tag; // 是否找到
}Result;
// 初始化树
Status InitBTree(BTree &T);
/* 初始化树
dataList:初始数据列表
*/
Status InitBTree(BTree &T, DataType *dataList, int dataNum);
// 把数据插入树
Status InsertData(BTree &T, DataType data);
// 根据关键字寻找数据插入位置
Result SearchInsertPos(BTree T, KeyType key);
/* 把数据加入某个结点
T:此结点所属的树
inPos:插入位置信息
data:需要插入的数据
subNewNode:子结点因数据过多分裂产生的新结点
*/
Status DataJoinNode(BTree &T, Result inPos, DataType data, BTNode* &subNewNode);
// 根据关键字查找数据
DataType SearchDataByKey(BTree T, KeyType key);
// 根据关键字查找数据的位置
Result SearchData(BTree T, KeyType key);
// 根据数据位置删除数据
Status DeleteData(BTree &T, Result dPos);
/* 在树T中合并父节点,当前结点(iNode)和其左右兄弟结点
T:树
pNode:iNode的双亲结点
lNode:iNode的左兄弟结点
iIndex:iNode在pNode中下标
rNode:iNode的右兄弟结点
*/
Status MergeNode(BTree &T, BTNode* pNode, BTNode* lNode, int iIndex, BTNode* rNode);
// 找到以rootNode为根的树的最后一个结点
BTNode* FindLastNode(BTNode* rootNode);
// 根据关键字删除数据
Status DeleteData(BTree &T, KeyType key);
// 按升序输出BTree的关键字
void PrintBTree(BTree T);
2、BTree.cpp
#include<iostream>
#include"BTree.h"
using namespace std;
Status InitNewNode(BTNode* &newNode) {
newNode = new BTNode;
newNode->keyNum = 0;
newNode->childNum = 0;
newNode->parent = NULL;
for (int i = 0; i < TREE_ORDER; i++) {
newNode->child[i] = NULL;
}
return OK;
}
Status InitBTree(BTree &T) {
if (TREE_ORDER > MAX_ORDER) {
cout << "B树阶数过大" << endl;
return ERROR;
}
else {
T = new BTNode;
T->keyNum = 0;
T->childNum = 0;
T->parent = NULL;
for (int i = 0; i < TREE_ORDER; i++) {
T->child[i] = NULL;
}
return OK;
}
}
Status InitBTree(BTree &T, DataType *dataList, int dataNum) {
if (InitBTree(T) == OK) {
for (int i = 0; i < dataNum; i++)
if (InsertData(T, dataList[i]) == ERROR)
return ERROR;
return OK;
}
return ERROR;
}
Status InsertData(BTree &T, DataType data) {
Result inPos = SearchInsertPos(T, data.key);
BTNode* subNewNode = NULL;
return DataJoinNode(T, inPos, data, subNewNode);
}
Result SearchInsertPos(BTree T, KeyType key) {
BTNode *p = NULL, *q = T;
int i = 0;
while (q) {
for (i = 0; i < q->keyNum && q->data[i].key < key; i++);
p = q;
q = q->child[i];
}
return {p, i, INSERT};
}
Status DataJoinNode(BTree &T, Result inPos, DataType data, BTNode* &subNewNode) {
BTNode *iNode = inPos.node;
int i = 0,k = 0;
if (iNode->keyNum < MAX_KEY_NUM) {// 情形1:直接插入
for (i = iNode->keyNum; i > inPos.pos; i--) {
iNode->data[i] = iNode->data[i - 1];
iNode->child[i + 1] = iNode->child[i];
}
iNode->data[i] = data;
iNode->keyNum += 1;
if (subNewNode) {
iNode->child[i + 1] = subNewNode;
subNewNode->parent = iNode;
iNode->childNum += 1;
}
}else { // 情形2:结点分裂
// 临时数组,保存插入关键字后此结点的数据
DataType temp[TREE_ORDER];
BTNode* tempNodes[TREE_ORDER + 1];
for (i = 0; i < inPos.pos; i++) {
temp[i] = iNode->data[i];
tempNodes[i] = iNode->child[i];
}
tempNodes[i] = iNode->child[i];
tempNodes[i + 1] = subNewNode;
temp[i] = data;
for (; i < iNode->keyNum; i++) {
temp[i + 1] = iNode->data[i];
tempNodes[i + 2] = iNode->child[i + 1];
}
/* 结点分裂
iNode保存左边信息,newNode保存右边信息
*/
iNode->childNum = 0;
for (i = 0; i < TREE_ORDER; i++) {
if (tempNodes[i] && i <= ROOT_INDEX) {
iNode->child[i] = tempNodes[i];
iNode->childNum++;
}
else {
iNode->child[i] = NULL;
}
}
iNode->keyNum = ROOT_INDEX;
// 创造一个新的结点
BTNode* newNode;
InitNewNode(newNode);
newNode->keyNum = MAX_KEY_NUM - ROOT_INDEX;
for (i = ROOT_INDEX + 1, k = 0; i <= TREE_ORDER; i++,k++) {
newNode->data[k] = temp[i];
if (tempNodes[i]) {
newNode->child[k] = tempNodes[i];
newNode->childNum += 1;
tempNodes[i]->parent = newNode;
}
}
BTNode *pNode = iNode->parent; //当前结点的父节点
if (pNode) { // 父节点不空,可以继续向上层进位
for (i = 0; i < pNode->keyNum && pNode->data[i].key < temp[ROOT_INDEX].key; i++);
Result pInPos = { pNode, i, INSERT };
DataJoinNode(T, pInPos, temp[ROOT_INDEX], newNode);
}else { // 父节点空,即当前结点是根,重新生成根节点
BTNode* rootNode;
InitNewNode(rootNode);
rootNode->childNum = 2;
rootNode->keyNum = 1;
rootNode->child[0] = iNode;
rootNode->child[1] = newNode;
rootNode->data[0] = temp[ROOT_INDEX];
newNode->parent = rootNode;
iNode->parent = rootNode;
T = rootNode;
}
}
return OK;
}
DataType SearchDataByKey(BTree T, KeyType key) {
BTNode* p = T;
int i;
while (p) {
for (i = 0; i < p->keyNum; i++) {
if (p->data[i].key == key)
return p->data[i];
else if (p->data[i].key > key)
break;
}
p = p->child[i];
}
return { {NOT_FOUND} };
}
Result SearchData(BTree T, KeyType key) {
BTNode* p = T;
int i;
while (p) {
for (i = 0; i < p->keyNum; i++) {
if (p->data[i].key == key)
return { p, i, SUCCESS };
else if (p->data[i].key > key)
break;
}
p = p->child[i];
}
return { NULL, -1, FAIL };
}
Status MergeNode(BTree &T, BTNode* pNode, BTNode* lNode, int iIndex, BTNode* rNode) {
if (!pNode || !T || iIndex < 0 || !rNode && !lNode)
return ERROR;
int i = 0;
BTNode* iNode = pNode->child[iIndex];
// 假设pNode->child[iIndex]指向iNode
if (rNode && rNode->keyNum > MIN_KEY_NUM) {
/* 如果rNode借位后关键字数目大于阈值
从rNode借位给iNode:假设pNode->child[i]指向iNode
*/
// (1)把pNode->data[iIndex]加入iNode;
iNode->data[iNode->keyNum++] = pNode->data[iIndex];
// (2)把rNode->data[0]代替pNode->data[iIndex]
pNode->data[iIndex] = rNode->data[0];
// (3)将rNode->child[0]并入iNode
if (rNode->child[0])
rNode->child[0]->parent = iNode;
iNode->child[iNode->childNum++] = rNode->child[0];
// (4)从rNode中删除data[0]和child[0]
for (i = 0; i < rNode->keyNum - 1; i++) {
rNode->data[i] = rNode->data[i + 1];
}
rNode->keyNum--;
if (rNode->childNum > 0) {
for (i = 0; i < rNode->childNum - 1; i++) {
rNode->child[i] = rNode->child[i + 1];
}
rNode->child[--rNode->childNum] = NULL;
}
}
else if (lNode && lNode->keyNum > MIN_KEY_NUM) {
/* 如果lNode借位后关键字数目大于阈值
从lNode借位给iNode:
*/
// (1)把pNode->data[iIndex - 1]加入iNode;
for (i = iNode->keyNum; i > 0; i--)
iNode->data[i] = iNode->data[i - 1];
iNode->data[0] = pNode->data[iIndex - 1];
iNode->keyNum++;
// (2)把lNode->data[end]代替pNode->data[iIndex - 1]并删除lNode->data[end]
pNode->data[iIndex - 1] = lNode->data[--lNode->keyNum];
// (3)将lNode->child[end]并入iNode并删除
if (iNode->childNum > 0) {
for (i = iNode->childNum; i > 0; i++)
iNode->child[i] = iNode->child[i - 1];
iNode->child[0] = lNode->child[--lNode->childNum];
iNode->child[0]->parent = iNode;
lNode->child[lNode->childNum] = NULL;
}
} else if (!pNode->parent && pNode->keyNum <= ROOT_MIN_KEY_NUM) { // pNode是根结点并且借位后关键字数目小于阈值
if (rNode && rNode->keyNum == MIN_KEY_NUM) {
/*
1、若rNode存在,则把iNode剩余信息和根结点信息合并至rNode;
2、令rNode为根节点
*/
// 将根结点和iNode剩余关键字并入rNode
for (i = rNode->keyNum - 1; i >= 0; i++)
rNode->data[i + MIN_KEY_NUM] = rNode->data[i];
for (i = 0; i < iNode->keyNum; i++) {
rNode->data[i] = iNode->data[i];
rNode->keyNum++;
}
rNode->data[i] = pNode->data[0];
rNode->keyNum++;
// 将iNode剩余的子结点挪至rNode
for (i = rNode->childNum - 1; i >= 0; i++)
rNode->child[i + MIN_KEY_NUM] = rNode->child[i];
for (i = 0; i < iNode->childNum && iNode->child[i]; i++) {
iNode->child[i]->parent = rNode; // 修改iNode子结点的双亲指针
rNode->child[i] = iNode->child[i];
rNode->childNum++;
}
delete iNode;
T = rNode;
}
else if (lNode && lNode->keyNum == MIN_KEY_NUM) {
/*
1、若lNode存在,则把iNode剩余信息和根结点信息合并至lNode;
2、令lNode为根节点
*/
// 将根结点和iNode剩余关键字并入lNode
lNode->data[lNode->keyNum++] = pNode->data[0];
for (i = 0; i < iNode->keyNum; i++)
lNode->data[lNode->keyNum++] = iNode->data[i];
// 将iNode剩余子结点并入lNode
for (i = 0; i < iNode->childNum && iNode->child[i]; i++) {
iNode->child[i]->parent = lNode;
lNode->child[lNode->childNum++] = iNode->child[i];
}
delete iNode;
T = lNode;
}
} else {
if (lNode) {
/*
lNode存在,从pNode借一位加上iNode剩余信息并入lNode
*/
//(1)pNode->data[iIndex - 1]和iNode剩余关键字并入lNode
lNode->data[lNode->keyNum++] = pNode->data[iIndex - 1];
for (i = 0; i < iNode->keyNum; i++)
lNode->data[lNode->keyNum++] = iNode->data[i];
for (i = iIndex - 1; i < pNode->keyNum - 1; i++)
pNode->data[i] = pNode->data[i + 1];
pNode->keyNum--;
//(2)如果iNode有子结点,并入lNode
if (lNode->childNum > 0) {
for (i = 0; i < iNode->childNum; i++) {
iNode->child[i]->parent = lNode;
lNode->child[lNode->childNum++] = iNode->child[i];
}
}
for (i = iIndex; i < pNode->childNum - 1; i++)
pNode->child[i] = pNode->child[i + 1];
pNode->child[--pNode->childNum] = NULL;
delete iNode;
}else {
/*
rNode存在,从pNode借一位加上iNode剩余信息并入rNode
*/
//(1)pNode->data[iIndex]和iNode剩余关键字并入rNode
for (i = rNode->keyNum - 1; i >= 0; i--)
rNode->data[i + MIN_KEY_NUM] = rNode->data[i];
rNode->data[i + MIN_KEY_NUM] = pNode->data[iIndex];
rNode->keyNum++;
for (i = 0; i < iNode->keyNum; i++) {
rNode->data[i] = iNode->data[i];
rNode->keyNum++;
}
for (i = iIndex; i < pNode->keyNum - 1; i++)
pNode->data[i] = pNode->data[i + 1];
pNode->keyNum--;
// (2)如果iNode有子结点,并入rNode
if (iNode->childNum > 0) {
for (i = rNode->childNum - 1; i >= 0; i--)
rNode->child[i + MIN_KEY_NUM] = rNode->child[i];
for (i = 0; i < iNode->childNum; i++) {
iNode->child[i]->parent = rNode;
rNode->child[i] = iNode->child[i];
rNode->childNum++;
}
}
for (i = iIndex; i < pNode->childNum - 1; i++)
pNode->child[i] = pNode->child[i + 1];
pNode->child[--pNode->childNum] = NULL;
delete iNode;
}
if (pNode->parent && pNode->keyNum < MIN_KEY_NUM) {
// 当pNode不是根节点且关键字数目小于阈值,将其双亲结点、左右兄弟结点合并
BTNode* ppNode = pNode->parent;
BTNode *plNode = NULL, *prNode = NULL;
for (i = 0; i < ppNode->childNum; i++)
if (ppNode->child[i] == pNode)
break;
if (i >= 1)
plNode = ppNode->child[i - 1];
if (i <= ppNode->childNum - 1)
prNode = ppNode->child[i + 1];
MergeNode(T, ppNode, plNode, i, prNode);
}
}
return OK;
}
Status DeleteData(BTree &T, Result dPos) {
int pos = dPos.pos;
BTNode* node = dPos.node;
if (node == NULL || pos < 0) {
cout << "删除出错" << endl;
return ERROR;
}else {
if (node->childNum > 0) {
// node是非终端结点,用其前驱代替之,然后删除前驱位置的关键字
BTNode* preNode = FindLastNode(node->child[pos]);
node->data[pos] = preNode->data[preNode->keyNum - 1];
DeleteData(T, { preNode, preNode->keyNum - 1, DELETE});
}else {
if (node->keyNum > MIN_KEY_NUM) { // 关键字数目大于阈值,直接删除
node->keyNum -= 1;
}else { // 关键字数目小于阈值,删除后合并
for (int i = pos; i < node->keyNum - 1; i++)
node->data[i] = node->data[i + 1];
node->keyNum--;
BTNode* pNode = node->parent;
if (pNode) { // node不是根节点,需要合并双亲结点、左右兄弟结点
BTNode *lNode = NULL, *rNode = NULL;
int iIndex = 0;
for (; iIndex < pNode->childNum; iIndex++) {
if (pNode->child[iIndex] == node)
break;
}
if (iIndex >= 1)
lNode = pNode->child[iIndex - 1];
if (iIndex <= pNode->childNum - 1)
rNode = pNode->child[iIndex + 1];
MergeNode(T, pNode, lNode, iIndex, rNode);
}
}
}
}
return OK;
}
Status DeleteData(BTree &T, KeyType key) {
Result res = SearchData(T, key);
if (res.tag == FAIL)
return ERROR;
else {
res.tag = DELETE;
DeleteData(T, res);
}
return OK;
}
BTNode* FindLastNode(BTNode* rootNode) {
BTNode* p = rootNode;
while (p->childNum > 0) {
p = p->child[p->childNum - 1];
}
return p;
}
void PrintBTree(BTree T) {
BTNode* p = T;
if (p->childNum == 0) {
for (int i = 0; i < p->keyNum; i++)
cout << p->data[i].key << ",";
}
else {
int i = 0;
for (; i < p->keyNum; i++) {
PrintBTree(p->child[i]);
cout<< p->data[i].key << ",";
}
PrintBTree(p->child[i]);
}
}
3、main.cpp
#include<iostream>
#include"BTree.h"
using namespace std;
int main() {
DataType dataList[15] = { {89}, {92}, {1},{3},{5},{6},{8},{9},{11},{13},{15},{22},{30},{35},{36} };
BTree tree;
InitBTree(tree, dataList, 15);
cout << "B树初始化完成后遍历结果:";
PrintBTree(tree);
cout << endl;
DeleteData(tree, 3);
cout << "删除关键字3后遍历结果:";
PrintBTree(tree);
cout << endl;
system("pause");
return 0;
}
4、运行结果: