算法导论B树的c++实现

来源:互联网 发布:广东元擎网络培训骗局 编辑:程序博客网 时间:2024/06/03 21:38

    算法导论的课要完成一个小课题,选了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                //case2x是内部结点,并且关键字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            // case2by只有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 个关键字,必须执行步骤3a3b来保证降至一个至少包含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中的关键字是在yz(如果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’

//这里的关键字ky的父结点中的关键字。

//其实仔细理解一下,就是找出以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个关键字,需要向左兄弟结点借一个,

// yz的左兄弟结点

// 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 <iostream>using namespace std;static const int t = 3;  // 设定最小度为3static 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 T>class BTree;template <class T>class Node{friend class BTree<T>;  // 友元函数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<T>* pChild[childMax];  // 子结点指针数组,叶子结点没有孩子,那就全设置成NULL好了};// B树类template <class T>class BTree{public:BTree();  // B树的构造函数Node<T>* search(Node<T>* node, T k);  // B树的搜索void splitChild(Node<T>* parentNode, int index);  // 分裂B树中的结点bool insert(T k);  // 以沿树单程下行方式向B树插入关键字void insertNonfull(Node<T>* node, T k);  // 结点非满时,插入关键字void printPart(Node<T>* node, int num);  // 横向打印以node为根的B树void printAll();    // 打印整棵B树// 接下来是恐怖的删除部分bool deleteKey(T k);  // 删除关键字void deleteNonone(Node<T>* node, T k);  // 对于不是特殊情况进行删除操作void mergeChild(Node<T>* node, int i);  // 合并T searchPredecessor(Node<T>* node);  // 找到前驱T searchSuccessor(Node<T>* node);    // 找到后继void shiftToRightChild(Node<T>* x, int i, Node<T>* y, Node<T>* z);  // 向左兄弟结点借一个关键字void shiftToLeftChild(Node<T>* x, int i, Node<T>* y, Node<T>* z);  // 向右兄弟结点借一个关键字private:Node<T>* root;  // B树的成员变量就只有这个根结点指针,有它就够了};// B树的构造函数template <class T>BTree<T>::BTree(){root = NULL;  // 初始化}/*函数名:search函数作用:搜索B树中的关键字函数参数:Node<T>* node(一个结点), T k(要搜索的关键字)函数返回值:Node<T>*, 如果找到,返回该结点,否则返回NULL*/template <class T>Node<T>* BTree<T>::search(Node<T>* 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]<k<key[i] 的,注意边界!!// 左边界,此时:k<key[i];右边界,此时:key[i-1]<k(此时i=keyNum)// 对应的,只要继续搜索pChild[i]就好了else{return search(node->pChild[i], k);}}}/*函数名:splitChild函数作用:分裂B树中的结点,该过程把一个满子结点分裂成两个,调整父结点,使之包含多出来的孩子函数参数:(Node<T>* parentNode, int index)parentNode表示一个非满的内部结点(作为父结点),index表示这个父结点的满子结点的下标函数返回值:无*/template <class T>void BTree<T>::splitChild(Node<T>* parentNode, int index){Node<T>* newNode = new Node<T>();  // 创建一个新的孩子结点Node<T>* 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-1if (!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 <class T>bool BTree<T>::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<T>();}// B树存在的情况下,如果根结点非满,可以直接使用insertNonfull// 但是根结点满的情况下,就要采取一下特殊的措施if (root->keyNum == keyMax){Node<T>* s = new Node<T>();  // 新创建一个结点,作为新的根结点s->leaf = false;  // 结点初始化时是true,所以要设置一下s->pChild[0] = root;  // 这样原来的满根结点就成了新结点s的孩子splitChild(s, 0);  // 此时要分裂的就是s结点的下标为0的孩子root = s;  // 分裂完成之后,更新根结点}// 不管上面两个if走不走,最后这个活都是要干的insertNonfull(root, k);return true;  // 到这儿说明插入成功}}/*先写非满结点的插入,再写上面的插入( ̄▽ ̄)~*函数名:insertNonfull函数作用:向一个非满的根结点中插入关键字函数参数:(Node<T>* node, T k)函数返回值:无*/template <class T>void BTree<T>::insertNonfull(Node<T>* node, T k){int i = node->keyNum - 1;  // 获取结点最大关键字的下标// 如果该结点就是叶子结点,那就直接插入到该结点if (node->leaf){while (i >= 0 && k < node->key[i])  // 循环结束时,key[i]<k<key[i+1]{node->key[i + 1] = node->key[i];i--;}node->key[i + 1] = k;  // 所以这里插入位置的下标是 i+1node->keyNum++;  // 最后让关键字个数加一}// 当该结点不是叶子结点时,继续向下一层else{while (i >= 0 && k < node->key[i]){i--;  // 当整个while循环结束后,key[i]<k<key[i+1]}i++;  // 所以要在这里将i加个一,此时:key[i-1]<k<key[i]if (node->pChild[i]->keyNum == keyMax)  // 当这个孩子结点满时,要先分裂它{splitChild(node, i);  // 分裂之后呢,node结点下标i以后的关键字都后移了,所以这里只能保证k<key[i]if (k > node->key[i])  // 由于上一句分裂的原因,这里需要比较{i++;  // 如果满足,下标i就要右移一位}}insertNonfull(node->pChild[i], k);}}/*函数名:printPart函数作用:横向打印函数参数:(Node<T>* node, int num),这里的num表示的是根结点前面打印的横杆数目函数返回值:无*/template <class T>void BTree<T>::printPart(Node<T>* 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 <class T>void BTree<T>::printAll(){printPart(root, 2);}// 删除部分的各个函数注释就不多写了,别问为什么,伪代码已经很详细了,最主要我不想写!不想写!!!!(T_T)/*函数名称:deleteKey函数作用:删除关键字k*/template <class T>bool BTree<T>::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<T>* leftChild = root->pChild[0];Node<T>* rightChild = root->pChild[1];if (leftChild->keyNum == keyMin && rightChild->keyNum == keyMin){mergeChild(root, 0);delete root;root = leftChild;}}}deleteNonone(root, k);}/*函数名称:deleteNonone函数作用:不知道(╯︵╰)*/template <class T>void BTree<T>::deleteNonone(Node<T>* 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<T>* rightChild = NULL;Node<T>* p = NULL;while (i < node->keyNum && k > node->key[i]){i++;}Node<T>* leftChild = node->pChild[i];if (i < node->keyNum){//Node<T>* 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<T>* 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 <class T>void BTree<T>::mergeChild(Node<T>* node, int i){Node<T>* leftChild = node->pChild[i];Node<T>* 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 <class T>T BTree<T>::searchPredecessor(Node<T>* node){Node<T>* 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 <class T>T BTree<T>::searchSuccessor(Node<T>* node){Node<T>* x = node;while (!x->leaf){x = x->pChild[0];}return x->key[0];}/*函数名称:shiftToRightChild函数作用:向左兄弟结点借一个关键字*/template <class T>void BTree<T>::shiftToRightChild(Node<T>* x, int i, Node<T>* y, Node<T>* 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 <class T>void BTree<T>::shiftToLeftChild(Node<T>* x, int i, Node<T>* y, Node<T>* 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 <iostream>using namespace std;#include "BTree.h"int main(void){BTree<char> *bt1 = new BTree<char>();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)

原创粉丝点击