算法导论的课要完成一个小课题,选了B树,花了挺长时间弄完,之前写了几个简单的排序,发现这递归是真的牛批啊。代码部分参考了这位博主。在写前面的插入那一大块,书上有伪代码可以对着写。但是后面的删除部分没有伪代码,懵了好久,找到另一位博主写的伪代码,有几处小错误,我重新写了一遍,并加上了注释(自己理解的注释,可能有地方理解有误)。不多bb,直接上伪代码和最终源码
// 伪代码均以书上的标准对照着写的,下标均以1开始
// x表示父结点,i表示x中要向下合并的关键字下标
// 并且这里还必须保证父结点关键字个数至少是t个(当然根结点除外),左右两个子结点的关键字个数是t-1
B-TREE-MERGE-CHILD(x,i)
1 y=x.ci //获取左子结点
2 z=x.ci+1 //获取右子结点
3 y.n=2t-1 // 设置左子结点的关键字个数为最大个数
4 y.keyt=x.keyi // 转移父结点要合并的关键字
5 for j=t+1to 2t-1 // 注意下标,转移关键字个数是t-1个
6 y.keyj=z.keyj-t // 转移右子结点的关键字
7 if noty.leaf
8 for j=t+1 to 2t // 孩子结点指针个数是t个
9 y.cj=z.cj-t
10 for j=i+1 to x.n // 父结点在i之后的、孩子结点指针都要前移
11 x.keyj-1=x.keyj // 关键字前移一位
12 x.cj=x.cj+1 // 孩子结点指针前移一位
13 x.n=x.n-1 //最后要将父结点的关键字个数减一
14 FREE-NODE(z)
15 DISK-WRITE(x)
16 DISK-WRITE(y)
//这里对根结点关键字只有1个,并且两个子结点关键字个数都是t-1,进行特殊处理
B-TREE-DELETE(T,k)
1 r=T.root
2 ifr.n==1
3 DISK-READ(x,c1)
4 DISK-READ(x,c2)
5 y=r.c1
6 z=r.c2
7 if y.n==z.n==t-1 // case 2c or 3b其实为什么2c也是这种情况,我也弄不明白,书上写的“可能出现”
8 B-TREE-MERGE-CHILD(r,1)
9 T.root=y
10 FREE-NODE(r)
11 B-TREE-DELETE-NONONE(y,k)
12 elseB-TREE-DELETE-NONONE(r,k)
13 else B-TREE-DELETE-NONONE(r,k)
// NONONE,以我的理解就是非上面的DELETE中处理的特殊情况。即根结点关键字只有1个,并且两个子结点关键字个数都是t-1。
B-TREE-DELETE-NONONE(x,k)
1 i=1
2 ifx.leaf // case1:关键字k在结点x中,并且x叶节点。疑问,如果该叶子结点关键字个数是t-1个呢(后面有回答,尽请关注)
3 while i<=x.n and k>x.keyi
4 i=i+1
5 if k==x.keyi
6 for j=i to x.n-1 //前移后面的关键字
7 x.keyj=x.keyj+1
8 x.n=x.n-1
9 DISK-WRITE(x)
10 elsewhile i<=x.n and k>x.keyi //x是内部结点
11 i=i+1
12 DISK-READ(x.ci) //上面的while结束后,k<=x.keyi(除了i=x.n+1)。如果k!=x.keyi,那么k必然在以x.ci为根的子树中。
13 y=x.ci //这里i有可能是x.n+1
14 ifi<=x.n //当然这里感觉是有个问题的,如果i=x.n+1的话,
15 DISK-READ(x.ci+1) //就无法访问到x.ci+1
16 z=x.ci+1 // z也就不存在,c++中,后面判断z.n>t-1那里不知道会不会出错
17 ifk==x.keyi //case2:x是内部结点,并且关键字k在结点x中
18 ify.n>t-1 // case2a:结点x中前于k的子结点y至少包含t个关键字
19 k’=B-TREE-SEARCH-PREDECESSOR(y)
20 B-TREE-DELETE-NONONE(y,k’) //递归地删除k’
21 x.keyi=k’ //并在x中用k’代替k
22 elseif z.n>t-1 // case2b:y只有t-1个关键字,检查后于k的子结点z。就是这里,如果z不存在,这里的判断条件语句有木有问题呢?不晓得=-=
23 k’=B-TREE-SEARCH-SUCCESSOR(z)
24 B-TREE-DELETE-NONONE(z,k’)
25 x.keyi=k’
26 elseB-TREE-MERGE-CHILD(x,i) // case2c:y和z都只含有t-1个关键字,将k和z的全部合并进y
27 B-TREE-DELETE-NONONE(y,k) // 递归地从y中删除k
28 else // case3:关键字k不在内部结点x中,必然在以y(即x.ci)为根的子树中
29 ifi>1 //获取左结点时必须左边有兄弟结点
30 DISK-READ(x.ci-1) //读取y结点的左兄弟结点。z就是y的右兄弟结点
31 p=x.ci-1
32 ify.n=t-1 //如果x.ci(即y)只有 t-1 个关键字,必须执行步骤3a或3b来保证降至一个至少包含t个关键字的结点。这样就保证了遍历的结点关键字个数都是大于t-1,这样就解决了直接在叶子结点删除关键字的疑惑,当递归到叶子结点时,该叶子结点关键字个数必定大于t-1
33 ifi>1 and p.n>t-1 // case3a:左兄弟结点作出牺牲。因为必须要存在左兄弟结点,所以条件i>1必不可少
34 B-TREE-SHIFT-TO-RIGHT-CHILD(x,i-1,p,y) //注意这里的参数,当前的下标i指向x中的关键字是在y和z(如果z存在)之间的
35 else if i<=x.nand z.n>t-1 // case3a:右兄弟结点作出牺牲。这里也一样,因为要存在右兄弟结点,所以i<=x.n
36 B-TREE-SHIFT-TO-LEFT-CHILD(x,i,y,z) //这里的第二个参数就应该是i了,不用做手脚0.0
37 elseif i>1 // case3b:如果说上面两种情况都不存在,也就是y结点的左右两个兄弟结点的关键字个数都只有t-1个关键字,将y(即x.ci)与一个兄弟结点合并。这条支路是左兄弟结点存在的情况下,x的第 (i-1) 个关键字和y合并到左兄弟结点
38 B-TREE-MERGE-CHILD(x,i-1) // 注意这里下标是i-1。
39 y=p // 而且合并之后p就是最终合并成的结点,要把y更新一下
40 elseB-TREE-MERGE-CHILD(x,i) // case3b:与右兄弟结点合并。这里合并最后生成的是y结点,所以不用更新y
41 B-TREE-DELETE-NONONE(y,k) // 最后递归删除
//找出关键字k在以y为根的子树中的前驱k’,
//这里的关键字k是y的父结点中的关键字。
//其实仔细理解一下,就是找出以y为根的子树中的最大关键字
//刚开始我的理解是找到关键字k左边的孩子结点中的最大的关键字,但是这样是不正确的,应该把这个子树中最大的那个关键字提升到k的位置,否则B树性质不保
B-TREE-SEARCH-PREDECESSOR(y)
1 x=y
2 i=x.n
3 whilenot x.leaf
4 DISK-READ(x.ci+1)
5 x=x.ci+1
6 i=x.n
7 returnx.keyi
//找到后继
B-TREE-SEARCH-SUCCESSOR(z)
1 x=z
2 whilenot x.leaf
3 DISK-READ(x.c1)
4 x=x.c1
5 return x.key1
// z为主角,它原本只有t-1个关键字,需要向左兄弟结点借一个,
// y为z的左兄弟结点
// x为父结点,把x中第i个关键字移到z中首个,y中最大的关键字移到x中
//并且还要将z中的关键字和孩子指针(如果不是叶子结点的话)后移一位
//还要将y中最后一个孩子指针(如果不是叶结点)移到z中的第一个孩子指针
B-TREE-SHIFT-TO-RIGHT-CHILD(x,i,y,z)
1 z.n=z.n+1
2 j=z.n
3 whilej>1
4 z.keyj=z.keyj-1
5 j=j-1
6 z.key1=x.keyi
7 x.keyi=y.keyy.n //转移y结点最后一个关键字到x中
8 if notz.leaf
9 j=z.n
10 whilej>0
11 z.cj+1=z.cj
12 j=j-1
13 z.c1=y.cy.n+1 //转移y的最后一个孩子指针到z的第一个孩子指针
14 y.n=y.n-1 //最后修改一下y的关键字个数
15 DISK-WRITE(x)
16 DISK-WRITE(y)
17 DISK-WRITE(z)
//从右兄弟结点借一个关键字
B-TREE-SHIFT-TO-LEFT-CHILD(x,i,y,z)
1 y.n=y.n+1
2 y.keyy.n=x.keyi
3 x.keyi=z.key1
4 z.n=z.n-1
5 j=1
6 whilej<=z.n
7 z.keyj=z.keyj+1
8 j=j+1
9 if notz.leaf
10 y.cy.n+1=z.c1
11 j=1
12 whilej<=z.n+1
13 z.cj=z.cj+1
14 j=j+1
15 DISK-WRITE(x)
16 DISK-WRITE(y)
17 DISK-WRITE(z)
#include
using namespace std;
static const int t = 3; // 设定最小度为3
static const int keyMax = 2 * t - 1; // 关键字最大个数
static const int keyMin = t - 1; // 关键字最小个数
static const int childMax = keyMax + 1; // 子结点最大个数
static const int childMin = keyMin + 1; // 子结点最小个数
// 前置声明,否则使用在Node类中使用friend会出问题
template
class BTree;
template
class Node
{
friend class BTree; // 友元函数
public:
// 类模板好像是不能分文件定义的,Emmm...那就直接在里面写吧
Node() // 构造函数,也就是生成(初始化)一个一个新结点
{
keyNum = 0;
leaf = true; // 新结点当然是叶子结点
// 注意下面数组的下标都是从0开始,书上都是从1开始的,当心当心!!!
for (int i = 0; i < keyMax; i++)
{
key[i] = '\0'; // 初始化将每个关键字设为结束符
}
for (int i = 0; i < childMax; i++)
{
pChild[i] = NULL; // 初始化将每个子结点指针设为NULL
}
}
private:
int keyNum; // 存在的关键字个数
bool leaf; // 结点是否为叶子结点
// 下面的数组直接将大小指定为最大了,
// c++数组不支持随时更改大小
// 后面用不到的元素设置为空便好
T key[keyMax]; // 关键字本身
Node* pChild[childMax]; // 子结点指针数组,叶子结点没有孩子,那就全设置成NULL好了
};
// B树类
template
class BTree
{
public:
BTree(); // B树的构造函数
Node* search(Node* node, T k); // B树的搜索
void splitChild(Node* parentNode, int index); // 分裂B树中的结点
bool insert(T k); // 以沿树单程下行方式向B树插入关键字
void insertNonfull(Node* node, T k); // 结点非满时,插入关键字
void printPart(Node* node, int num); // 横向打印以node为根的B树
void printAll(); // 打印整棵B树
// 接下来是恐怖的删除部分
bool deleteKey(T k); // 删除关键字
void deleteNonone(Node* node, T k); // 对于不是特殊情况进行删除操作
void mergeChild(Node* node, int i); // 合并
T searchPredecessor(Node* node); // 找到前驱
T searchSuccessor(Node* node); // 找到后继
void shiftToRightChild(Node* x, int i, Node* y, Node* z); // 向左兄弟结点借一个关键字
void shiftToLeftChild(Node* x, int i, Node* y, Node* z); // 向右兄弟结点借一个关键字
private:
Node* root; // B树的成员变量就只有这个根结点指针,有它就够了
};
// B树的构造函数
template
BTree::BTree()
{
root = NULL; // 初始化
}
/*
函数名:search
函数作用:搜索B树中的关键字
函数参数:Node* node(一个结点), T k(要搜索的关键字)
函数返回值:Node*, 如果找到,返回该结点,否则返回NULL
*/
template
Node* BTree::search(Node* node, T k)
{
if (node == NULL) // 如果这是一个空树的话,根节点是指向NULL的
{
return NULL;
}
// 根据结点的孩子数做多路分支选择,
// 对每个内部结点,做(keyNum+1)路的分支选择
else
{
int i = 0; // 下标是从0开始
// i的判断条件是要小于keyNum的,
// 当i=keyNum时,就会去搜索该结点的最后一个子结点
while(i < node->keyNum && k > node->key[i])
{
i++;
}
// 当while循环结束后,分为三种情况
// 第一种i仍然小于keyNum,并且key[i]等于k,那便是找到了
if (i < node->keyNum && k == node->key[i])
{
return node;
}
// 第二种:如果不满足第一种情况,也就是在该结点中没有找到关键k
// 首先要判断该结点是否为叶子节点,如果是,那就是没有
else if (node->leaf)
{
return NULL;
}
// 第三种:在该结点中没有找到关键字k,并且该结点不是叶子节点
// 那便要继续向下寻找,
// 此时的i,正好是满足 key[i-1]pChild[i], k);
}
}
}
/*
函数名:splitChild
函数作用:分裂B树中的结点,该过程把一个满子结点分裂成两个,调整父结点,使之包含多出来的孩子
函数参数:(Node* parentNode, int index)
parentNode表示一个非满的内部结点(作为父结点),
index表示这个父结点的满子结点的下标
函数返回值:无
*/
template
void BTree::splitChild(Node* parentNode, int index)
{
Node* newNode = new Node(); // 创建一个新的孩子结点
Node* oldNode = parentNode->pChild[index]; // 原来的满子结点,就是要分裂它,烧死它
newNode->leaf = oldNode->leaf;
newNode->keyNum = keyMin; // 分裂之后两个孩子都有最小的关键字个数
// oldNode中原本是有keyMax(2t-1)个关键字的(满的),
// 前面t-1个小的仍然留给旧子结点,后面t-1个大的给新子结点,最中间的关键字是要提升到父节点中去的
// 把oldNode中keyMin(t-1)个大的关键字放到newNode中。中间关键字下标为t-1(keyMin)
for (int i = 0; i < keyMin; i++)
{
newNode->key[i] = oldNode->key[keyMin + 1 + i]; // 下标开始为t,即keyMin + 1
}
// 如果该子结点不是叶子结点的话,还要转移一下该子结点的孩子结点指针
// oldNode原来孩子有childMax(2t)个,分裂后孩子是对半分的
// 前面t个孩子下标 0~t-1,后面t个孩子下标t~2t-1
if (!oldNode->leaf)
{
for (int i = 0; i < childMin; i++)
{
newNode->pChild[i] = oldNode->pChild[childMin + i];
}
}
oldNode->keyNum = keyMin; // 调整oldNode的关键字个数为keyMin(t-1)
// 要把父结点下标在index之后的孩子结点全部后移一位
// 分裂之后,父结点的keyNum要多一个,孩子结点个数要多一个(变为keyNum+2)
for (int i = parentNode->keyNum; i > index; i--)
{
parentNode->pChild[i + 1] = parentNode->pChild[i]; // 所以这里最后一个下标要从 keyNum+1 开始
}
parentNode->pChild[index + 1] = newNode; // 那么index的下一个孩子结点就是newNode
// 下标从index开始的关键字也通通要后移
for (int i = parentNode->keyNum - 1; i >= index; i--)
{
parentNode->key[i + 1] = parentNode->key[i];
}
parentNode->key[index] = oldNode->key[keyMin]; // 把原来要分裂的子节点中要提升的关键字放到父结点的index位置
parentNode->keyNum++;
}
/*
函数名:insert
函数作用:插入关键字
函数参数:T k
函数返回值:bool类型判断插入是否成功
*/
template
bool BTree::insert(T k)
{
if (search(root, k) != NULL) // 在B树中搜索该关键字,如果返回的不是NULL,说明该关键字存在
{
cout << k << " 已存在" << endl;
return false;
}
else
{
// 这里要先判断一下根节点是否指向NULL,在BTree的构造函数中,让root指向NULL了
if (root == NULL)
{
root = new Node();
}
// B树存在的情况下,如果根结点非满,可以直接使用insertNonfull
// 但是根结点满的情况下,就要采取一下特殊的措施
if (root->keyNum == keyMax)
{
Node* s = new Node(); // 新创建一个结点,作为新的根结点
s->leaf = false; // 结点初始化时是true,所以要设置一下
s->pChild[0] = root; // 这样原来的满根结点就成了新结点s的孩子
splitChild(s, 0); // 此时要分裂的就是s结点的下标为0的孩子
root = s; // 分裂完成之后,更新根结点
}
// 不管上面两个if走不走,最后这个活都是要干的
insertNonfull(root, k);
return true; // 到这儿说明插入成功
}
}
/*
先写非满结点的插入,再写上面的插入( ̄▽ ̄)~*
函数名:insertNonfull
函数作用:向一个非满的根结点中插入关键字
函数参数:(Node* node, T k)
函数返回值:无
*/
template
void BTree::insertNonfull(Node* node, T k)
{
int i = node->keyNum - 1; // 获取结点最大关键字的下标
// 如果该结点就是叶子结点,那就直接插入到该结点
if (node->leaf)
{
while (i >= 0 && k < node->key[i]) // 循环结束时,key[i]key[i + 1] = node->key[i];
i--;
}
node->key[i + 1] = k; // 所以这里插入位置的下标是 i+1
node->keyNum++; // 最后让关键字个数加一
}
// 当该结点不是叶子结点时,继续向下一层
else
{
while (i >= 0 && k < node->key[i])
{
i--; // 当整个while循环结束后,key[i]pChild[i]->keyNum == keyMax) // 当这个孩子结点满时,要先分裂它
{
splitChild(node, i); // 分裂之后呢,node结点下标i以后的关键字都后移了,所以这里只能保证k node->key[i]) // 由于上一句分裂的原因,这里需要比较
{
i++; // 如果满足,下标i就要右移一位
}
}
insertNonfull(node->pChild[i], k);
}
}
/*
函数名:printPart
函数作用:横向打印
函数参数:(Node* node, int num),这里的num表示的是根结点前面打印的横杆数目
函数返回值:无
*/
template
void BTree::printPart(Node* node, int num)
{
if (node != NULL) // 不要忽略了刚开始根结点指向空的情况
{
// 从根结点开始遍历,目的是结点中的关键字。但是,每遇到内部结点时,就要向下递归,
for (int i = 0; i < node->keyNum; i++)
{
// 当结点为内部结点时,递归到下面,当然在这个for循环中会缺少最右边孩子结点的情况
if (!node->leaf) // 注意这里必须先判断是否为内部结点而是否进行向下递归
{
printPart(node->pChild[i], num + 5); // 递归到下一层时,把num值每次增加5,这样就可以打印类似横向树状的模样
}
for (int j = 0; j < num; j++) // 打印当前层的结点的关键字
{
cout << "-"; // 第一层,即根结点一层,打印个数由调用时决定。但每次往下一层,个数增加5
}
cout << node->key[i] << endl;
}
if (!node->leaf) // 在for循环中无法递归到最右边的孩子结点,这里必须来这一下
{
printPart(node->pChild[node->keyNum], num + 5);
}
}
}
/*
函数名称:printAll
函数作用:打印整棵树
无参无返回值
*/
template
void BTree::printAll()
{
printPart(root, 2);
}
// 删除部分的各个函数注释就不多写了,别问为什么,伪代码已经很详细了,最主要我不想写!不想写!!!!(T_T)
/*
函数名称:deleteKey
函数作用:删除关键字k
*/
template
bool BTree::deleteKey(T k)
{
if (search(root, k) == NULL) // 如果B树中没有该关键字,还删个屁啊
{
return false;
}
if (root->keyNum == 1)
{
if (root->leaf) // 处理一下根结点是叶子结点的情况?
{
delete root;
root = NULL;
return true;
}
else
{
Node* leftChild = root->pChild[0];
Node* rightChild = root->pChild[1];
if (leftChild->keyNum == keyMin && rightChild->keyNum == keyMin)
{
mergeChild(root, 0);
delete root;
root = leftChild;
}
}
}
deleteNonone(root, k);
}
/*
函数名称:deleteNonone
函数作用:不知道(╯︵╰)
*/
template
void BTree::deleteNonone(Node* node, T k)
{
int i = 0;
if (node->leaf) // case 1
{
while (i < node->keyNum && k > node->key[i])
{
i++;
}
if (node->key[i] == k)
{
for (int j = i; j < node->keyNum - 1; j++)
{
node->key[j] = node->key[j + 1];
}
node->keyNum--;
}
}
else
{
Node* rightChild = NULL;
Node* p = NULL;
while (i < node->keyNum && k > node->key[i])
{
i++;
}
Node* leftChild = node->pChild[i];
if (i < node->keyNum)
{
//Node* rightChild = node->pChild[i + 1]; // 这里rightChild作用域的原因,所以我们可以在大的作用域中先定义一下
rightChild = node->pChild[i + 1];
}
if (node->key[i] == k) // case 2
{
if (leftChild->keyNum > keyMin) // case 2a
{
T k1 = searchPredecessor(leftChild);
deleteNonone(leftChild, k1);
node->key[i] = k1;
}
else if (rightChild->keyNum > keyMin) // case 2b
{
T k1 = searchSuccessor(rightChild);
deleteNonone(rightChild, k1);
node->key[i] = k1;
}
else // case 2c
{
mergeChild(node, i);
deleteNonone(leftChild, k);
}
}
else // case 3
{
if (i > 0)
{
//Node* p = node->pChild[i - 1];
p = node->pChild[i - 1];
}
if (leftChild->keyNum == keyMin)
{
if (i > 0 && p->keyNum > keyMin) // case 3a
{
shiftToRightChild(node, i - 1, p, leftChild);
}
else if (i < node->keyNum && rightChild->keyNum > keyMin) // case 3a
{
shiftToLeftChild(node, i, leftChild, rightChild);
}
else if (i > 0) // case 3b
{
mergeChild(node, i - 1);
leftChild = p;
}
else // case 3b
{
mergeChild(node, i);
}
}
deleteNonone(leftChild, k);
}
}
}
/*
函数名称:mergeChild
函数作用:合并结点
*/
template
void BTree::mergeChild(Node* node, int i)
{
Node* leftChild = node->pChild[i];
Node* rightChild = node->pChild[i + 1];
leftChild->keyNum = keyMax;
leftChild->key[keyMin]=node->key[i];
for (int j = keyMin + 1; j < keyMax; j++)
{
leftChild->key[j] = rightChild->key[j - (keyMin + 1)];
}
if (!leftChild->leaf)
{
for (int j = childMin; j < childMax; j++)
{
leftChild->pChild[j] = rightChild->pChild[j-childMin];
}
}
for (int j = i + 1; j < node->keyNum; j++)
{
node->key[j - 1] = node->key[j];
node->pChild[j] = node->pChild[j + 1];
}
node->keyNum--;
}
/*
函数名称:searchPredecessor
函数作用:找到前驱
函数返回值:T
*/
template
T BTree::searchPredecessor(Node* node)
{
Node* x = node;
int i = x->keyNum; // 这个下标即x的最后一个孩子指针
while (!x->leaf)
{
x = x->pChild[i];
i = x->keyNum;
}
return x->key[i - 1];
}
/*
函数名称:searchSuccessor
函数作用:找到后继
函数返回值:T
*/
template
T BTree::searchSuccessor(Node* node)
{
Node* x = node;
while (!x->leaf)
{
x = x->pChild[0];
}
return x->key[0];
}
/*
函数名称:shiftToRightChild
函数作用:向左兄弟结点借一个关键字
*/
template
void BTree::shiftToRightChild(Node* x, int i, Node* y, Node* z)
{
z->keyNum++;
int j = z->keyNum - 1; // 程序中下标从0开始
while (j > 0)
{
z->key[j] = z->key[j - 1];
j--;
}
z->key[0] = x->key[i];
x->key[i] = y->key[y->keyNum - 1];
if (!z->leaf)
{
j = z->keyNum - 1;
while (j >= 0)
{
z->pChild[j + 1] = z->pChild[j];
j--;
}
z->pChild[0] = y->pChild[y->keyNum];
}
y->keyNum--;
}
/*
函数名称:shiftToLeftChild
函数作用:向右兄弟结点借一个关键字
*/
template
void BTree::shiftToLeftChild(Node* x, int i, Node* y, Node* z)
{
y->keyNum++;
y->key[y->keyNum - 1] = x->key[i];
x->key[i] = z->key[0];
z->keyNum--;
int j = 0;
while (j < z->keyNum)
{
z->key[j] = z->key[j + 1];
j++;
}
if (!z->leaf)
{
y->pChild[y->keyNum] = z->pChild[0];
j = 0;
while (j <= z->keyNum)
{
z->pChild[j] = z->pChild[j + 1];
j++;
}
}
}
#include
using namespace std;
#include "BTree.h"
int main(void)
{
BTree *bt1 = new BTree();
bt1->insert('E');
bt1->insert('J');
bt1->insert('D');
bt1->insert('G');
bt1->insert('K');
bt1->insert('N');
bt1->insert('O');
bt1->insert('M');
bt1->insert('P');
bt1->insert('Z');
bt1->insert('T');
bt1->insert('X');
bt1->insert('R');
bt1->insert('Y');
bt1->insert('U');
bt1->insert('C');
bt1->insert('V');
bt1->insert('S');
bt1->insert('A');
// 插入部分
bt1->insert('B');
/*cout << "插入B:" << endl;
bt1->printAll();
cout << endl << endl;*/
bt1->insert('Q');
/*cout << "插入Q:" << endl;
bt1->printAll();
cout << endl << endl;*/
bt1->insert('L');
/*cout << "插入L:" << endl;
bt1->printAll();
cout << endl << endl;*/
bt1->insert('F');
/*cout << "插入F:" << endl;
bt1->printAll();
cout << endl << endl;*/
// 删除部分
bt1->deleteKey('F');
cout << "删除F:" << endl;
bt1->printAll();
cout << endl << endl;
bt1->deleteKey('M');
cout << "删除M:" << endl;
bt1->printAll();
cout << endl << endl;
bt1->deleteKey('G');
cout << "删除G:" << endl;
bt1->printAll();
cout << endl << endl;
bt1->deleteKey('D');
cout << "删除D:" << endl;
bt1->printAll();
cout << endl << endl;
bt1->deleteKey('B');
cout << "删除B:" << endl;
bt1->printAll();
cout << endl << endl;
system("pause");
return 0;
}
骚话:
今天我爱你
递归到明天
终止条件(I not EXIST)