算法导论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 //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 <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)
- 算法导论B树的c++实现
- 【算法导论】B树
- 算法导论B树
- 算法导论 B树
- 算法导论-B树
- 【算法导论】18.1:B树的定义
- 《算法导论》中红黑树的C语言实现
- 算法导论 之 B树 - 删除[C语言]
- 算法导论第十八章-B树-Cpp代码实现
- 算法导论 ch18 B树
- 【算法导论】B树讲解
- 算法导论—B树
- 算法导论之B树
- 算法导论: 附录A,B, C
- 算法导论-----排序的9种实现(C/C++)
- 算法导论-B树的插入与删除
- 算法导论 之 B-树(B树) - 创建、插入[C语言]
- 算法导论-AVL树的C++实现
- Ubuntu上出现无法获得锁“Could not get lock /var/lib/apt/lists/lock”问题的解决
- .project文件
- Max Script 入门教程
- 从2013到2017 CGU 草根战队的电竞梦
- 春花秋月何时了 MVP模式知多少
- 算法导论B树的c++实现
- 25年的思考沉淀,只为成就这一款经典
- PHP 策略者模式 清晰总结
- Shiro Odd number of characters.
- SQLServer根据表名创建三层架构的Model
- JSP网上购物系统//用户登入数据库连接
- ubuntu下php5.6安装
- 代码设置RelativeLayout相对位置设置
- QT 乱码问题