B树的实现算法
/***************************以下为存储结构及各种类型的定义*****************************/
//--------------------------------------------------------------------------------------
//定义B树的阶,暂定为3
//--------------------------------------------------------------------------------------
#define m 4
//--------------------------------------------------------------------------------------
//定义存储的关键字类型
//--------------------------------------------------------------------------------------
typedef int KeyType;
//--------------------------------------------------------------------------------------
//定义函数的返回类型
//--------------------------------------------------------------------------------------
typedef enum Status {
FALSE,TRUE
}Status;
//--------------------------------------------------------------------------------------
//定义存储结构
//--------------------------------------------------------------------------------------
typedef struct BTNode {
int keynum; //当前节点拥有的关键字的个数
KeyType key[m + 1]; //关键字,key[0]未用
struct BTNode *parent; //双亲结点指针
struct BTNode *ptr[m + 1]; //孩子结点指针数组
}BTNode,*BTree;
//--------------------------------------------------------------------------------------
//定义查找的结构类型
//--------------------------------------------------------------------------------------
typedef struct {
BTree pt; //指向找到的结点
int i; //1<=i<=m,在结点中的关键字位序
int tag; //1:查找成功,0:查找失败
}result;
/***************************以下为各个操作的思路及实现*****************************/
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//查找
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
实现思路:
(1) 若给定关键字等于结点中某个关键字Ki,则查找成功。
(2) 若给定关键字比结点中的k1笑,则进入指针A0指向的下一层结点继续查找。
(3) 若在两个关键字ki-1和ki之间,则进入它们之间的指针Ai指向的下一层结点继续查找。
(4) 若比该结点所有关键字大,则在其最后一个指针An指向的下一层结点继续查找;
(5) 若查找到叶子结点,则说明给定值对应的数据记录不存在,则查找失败。
代码:
1 //-------------------------------------------------------------------------------------- 2 //函数名...............:Search(BTree p,int k) 3 //功能.................:用于查找关键字在某个结点中的位置或待插入位置 4 //-------------------------------------------------------------------------------------- 5 int Search(BTree p, int k) 6 { 7 int i = 1; 8 while (i <= p->keynum && k > p->key[i]) 9 i++; 10 11 return i; 12 } 13 14 //-------------------------------------------------------------------------------------- 15 //函数名...............:SearchBTree(BTree,int k,result &r) 16 //功能.................:用于查找关键字在整个树中的位置或待插入位置 17 //-------------------------------------------------------------------------------------- 18 void SearchBTree(BTree t, int k, result &r) 19 { 20 //在m阶树t上查找关键字k,返回(pt,i,tag) 21 //若查找成功,则特征值tag=1,指针pt所指结点中第i个关键字等于k;否则, 22 //特征值tag=0,等于k的关键字记录应插入在指针pt所指结点中第i-1个和第i个关键字间 23 int i = 0, found = 0; 24 BTree p = t, q = NULL; //初始,p指向待查结点,q指向p的双亲 25 while (p != NULL && 0 == found) 26 { 27 i = Search(p, k); //在p->key[1..keynum]找p->key[i-1]<k<=->key[i] 28 if (i > 0 && i<=p->keynum && p->key[i] == k) 29 found = 1; //找到待查关键字 30 else 31 { 32 q = p; 33 p = p->ptr[i - 1]; 34 } 35 } 36 37 if (1 == found) //查找成功 38 { 39 r.pt = p; 40 r.i = i; 41 r.tag = 1; 42 } 43 else //查找不成功,返回key的插入位置i 44 { 45
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//插入
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
实现思路:
利用查找过程查找关键字K的插入位置。若找到,则说明该关键字已经存在,直接返回;否则查找操作必失败于
某个最底层的非终端结点上。在该结点中插入,若其关键字总数n未达到m,算法结束;否则,需分裂结点。
分裂的方法是:生成一新结点,从中间位置把结点(不包括中间位置的关键字)分裂成两部分。前半部分留在旧结点中,
后半部分复制到新结点中,中间位置的关键字连同新结点的存储位置插入到父结点中。
如果插入后父结点的关键字个数也超过m-1,则要再分裂。这个向上分裂过程如果持续到根结点分裂为止,则会产生新的根结点。
实现代码:
1 //-------------------------------------------------------------------------------------- 2 //函数名...............:InsertBTree(BTree &t,int k,BTree q,int i) 3 //功能.................:用于关键字在整棵树的插入及调整 4 //-------------------------------------------------------------------------------------- 5 void InsertBTree(BTree &t, int k, BTree q, int i) 6 { 7 //在B树t上q结点的key[i-1]和key[i]之间插入关键字k 8 //若引起结点过大,则沿双亲指针进行必要的结点分裂调整,使t仍是m阶的B树 9 int x, s, finished = 0, neednewroot = 0; 10 BTree ap; 11 if (NULL == q) 12 newroot(t,NULL,k,NULL); 13 else 14 { 15 x = k; 16 ap = NULL; 17 while (0 == neednewroot && 0 == finished) 18 { 19 Insert(q, i, x, ap); //key和ap分别查到q->key[i]和q->ptr[i] 20 if (q->keynum < m) //插入完成 21 finished = 1; 22 else //分裂q结点 23 { 24 s = (m + 1) / 2; 25 split(q, s, ap); 26 x = q->key[s]; 27 28 if (q->parent != NULL) //在双亲结点中查找x的插入位置 29 { 30 q = q->parent; 31 i = Search(q, x); 32 } 33 else 34 neednewroot = 1; 35 } 36 }//while 37 if (1 == neednewroot) //t是空树或者根结点已分裂为q和ap结点 38 newroot(t,q, x, ap); //生成含信息(q,x,ap)的新的根结点t 39 } 40 } 41 42 //-------------------------------------------------------------------------------------- 43 //函数名...............:split(BTree &q,int s,BTree &ap) 44 //功能.................:用于插入后的结点分裂调整 45 //-------------------------------------------------------------------------------------- 46 void split(BTree &q, int s, BTree &ap) 47 { 48 //将q结点分裂成两个结点,前一半保留,后一半移入新结点ap 49 int i, j, n = q->keynum; 50 ap = (BTNode *)malloc(sizeof(BTNode)); //生成新结点 51 52 for (int k = 0;k < m + 1;k++) //为新开辟的结点所有子树都赋为空 53 ap->ptr[k] = NULL; 54 55 ap->ptr[0] = q->ptr[s]; 56 for ( i = s + 1, j = 1;i <= n;i++, j++) //后一半移入ap结点 57 { 58 ap->key[j] = q->key[i]; 59 ap->ptr[j] = q->ptr[i]; 60 } 61 62 ap->keynum = n - s; 63 ap->parent = q->parent; 64 65 for (i = 0;i <= n - s;i++) 66 if (ap->ptr[i]) 67 ap->ptr[i]->parent = ap; 68 q->keynum = s - 1; //q结点的前一半保留,修改keynum 69 } 70 71 //-------------------------------------------------------------------------------------- 72 //函数名...............:newroot(BTree &t,BTree p,int x,BTree ap) 73 //功能.................:用于生成新的根结点 74 //-------------------------------------------------------------------------------------- 75 void newroot(BTree &t, BTree p, int x, BTree ap) 76 { 77 t = (BTNode*)malloc(sizeof(BTNode)); 78 79 for (int k = 0;k < m + 1;k++) //为新开辟的结点所有子树都赋为空 80 t->ptr[k] = NULL; 81 82 t->keynum = 1; 83 t->ptr[0] = p; 84 t->ptr[1] = ap; 85 t->key[1] = x; 86 87 if (p != NULL) 88 p->parent = t; 89 90 if (ap != NULL) 91 ap->parent = t; 92 t->parent = NULL; //新根的双亲是空指针 93 } 94 95 //-------------------------------------------------------------------------------------- 96 //函数名...............:Insert(BTree &q,int i,int x,BTree ap) 97 //功能.................:用于在具体结点中插入关键字 98 //-------------------------------------------------------------------------------------- 99 void Insert(BTree &q, int i, int x, BTree ap) 100 { 101 int j, n = q->keynum; 102 for (j = n;j >= i;j--) 103 { 104 q->key[j + 1] = q->key[j]; 105 q->ptr[j + 1] = q->ptr[j]; 106 } 107 108 q->key[i] = x; 109 q->ptr[i] = ap; 110 111 if (ap != NULL) 112 ap->parent = q; 113 114 q->keynum++; 115 }
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//删除
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
实现思路:
在B树上删除关键字k的过程可利用前述的B树的查找过程找出该关键字所在的结点,然后根据k所在结点是否为最下层非终端结点有不同的处理方法。
(1) 如果被删除关键字所在结点的原关键字个数n>=(m/2)(向上取整),说明删除该关键字后该结点仍满足B树的定义。这种情况最为简单,只需从该结点中直接删除去关键字即可。
(2) 如果被删除关键字所在结点的关键字个数n等于(m/2)(向上取整)-1,说明删除该关键字后该结点将不满足B树的定义,需要调整。
调整过程为:如果其左兄弟结点中有“富余”的关键字,即与该结点相邻的右(或左)兄弟结点中的关键字数目大于(m/2)(向上取整)-1。则可将右(左)兄弟结点中最小(大)关键字上移至双亲结点。而将双亲结点中小(大)与该上移关键字的关键字下移至被删除关键字所在结点中。
(3) 如果相邻兄弟结点中没有富余的关键字,即相邻兄弟结点中的关键字数目均等于(m/2)(向上取整)-1。此时比较复杂,需把要删除关键字的结点与其左(或右)兄弟结点以及双亲结点中分割二者的关键字合并成一个结点,即在删除关键字后,该结点中剩余的关键字加指针,加上双亲结点中的关键字Ki一起,合并到Ai(即双亲结点中指向该删除关键字结点的左(右)兄弟结点的指针)所指的兄弟结点中去。如果因此使双亲结点中关键字个数小于(m/2)(向上取整)-1,则对此双亲结点做同样的处理。以至于可能直到对根结点做这样的处理而使整个树减少一层。
(4) 如果该结点不是最下层非终端结点,且被删关键字为该结点中第i个关键字key[i],则可以从指针ptr[i]所指的子树中找出位于最下层非终端结点的最小关键字Y,替代key[i],然后在最底层非终端结点中移除Y。因此,把在非终端结点删除关键字k的问题就变成了删除最下层非终端结点中的关键字的问题了。
实现代码:
1 //-------------------------------------------------------------------------------------- 2 //函数名...............:DeleteBTree(BTree &root,,BTree &p,int i) 3 //功能.................:用于整体删除一个关键字及调整,如果根结点改变了则保存下来 4 //-------------------------------------------------------------------------------------- 5 void DeleteBTree(BTree &root,BTree &p, int i) //删除B树上的p结点第i个关键字 6 { 7 if (p->ptr[i - 1] != NULL) //若不是最下层非终端结点 8 { 9 Successor(p, i); //由后继最下层非终端结点的最小关键字代替它 10 DeleteBTree(root,p, 1); //变成删除最下层非终端结点中的最小关键字 11 } 12 else //若是最下层非终端结点 13 { 14 Remove(p, i); //从结点p中删除key[i] 15 if (p->keynum < (m - 1) / 2) //删除后关键字个数小于(m-1) 16 Restore(root,p); //调整B树 17 } 18 } 19 20 //-------------------------------------------------------------------------------------- 21 //函数名...............:Successor(BTree &p,int i); 22 //功能.................:由后继最下层非终端结点的最小关键字代替它 23 //-------------------------------------------------------------------------------------- 24 void Successor(BTree &p, int i) 25 { 26 BTree leaf=p; //最下层非终端结点 27 if (NULL == p) 28 return; 29 leaf = leaf->ptr[i]; //找到p的ptr[i]所指的子树 30 while (NULL != leaf->ptr[0]) //找到最下层非终端结点 31 leaf = leaf->ptr[0]; 32 p->key[i] = leaf->key[1]; //用最小值替换要被删除的结点 33 p = leaf; //把p指向最下层的非终端结点,方便下个删除函数 34 //(可以在外部实现) 35 } 36 37 //-------------------------------------------------------------------------------------- 38 //函数名...............:Remove(BTree &p,int i) 39 //功能.................:从结点中删除key[i] 40 //-------------------------------------------------------------------------------------- 41 void Remove(BTree &p, int i) 42 { 43 int k; 44 for (k = i;k < p->keynum;k++) 45 { 46 p->key[k] = p->key[k + 1]; 47 p->ptr[k] = p->ptr[k + 1]; 48 } 49 p->keynum-=1; 50 } 51 52 //-------------------------------------------------------------------------------------- 53 //函数名...............:Restore(BTree &root,BTree &p) 54 //功能.................:调整B树,如果根结点改变了则保存下来 55 //-------------------------------------------------------------------------------------- 56 void Restore(BTree &root,BTree &p) 57 { 58 BTree father,leftBrother, rightBrother; //代表被删关键字所在结点的父结点、左兄弟和右兄弟 59 father = p->parent; 60 if (father != NULL) 61 { 62 //寻找被删除关键字所在结点的左右兄弟 63 int i; 64 for (i = 0;i <=father->keynum;i++) 65 if (father->ptr[i] == p) 66 break; 67 68 if (i > 0) 69 leftBrother = father->ptr[i - 1]; 70 else 71 leftBrother = NULL; 72 73 if (i < father->keynum) 74 rightBrother = father->ptr[i + 1]; 75 else 76 rightBrother = NULL; 77 78 //根据左右兄弟的关键字个数进行调整 79 if (NULL != leftBrother&&leftBrother->keynum>((m + 1) / 2 - 1)) //左兄弟有剩余关键字 80 { 81 for (int j = (p->keynum+1);j > 0;j--) //因为要从左兄弟借关键字,需要放到最小的位置上 82 { //所以所有关键字及子树指针向后移一位 83 if(j>1) 84 p->key[j] = p->key[j - 1]; 85 p->ptr[j] = p->ptr[j - 1]; 86 } 87 88 //对最左边的关键字及子树指针的处理 89 p->ptr[0] = leftBrother->ptr[leftBrother->keynum]; //从左兄弟借最大的关键字,所以需要 90 //把左兄弟中最后一个子树结点赋给新的子树指针 91 92 if(NULL!=p->ptr[0]) 93 p->ptr[0]->parent = p; //新借来的结点把其父亲指向改为自己 94 95 leftBrother->ptr[leftBrother->keynum] = NULL; //左兄弟最大的子树借出去之后把它的指向指为空 96 97 p->key[1] = father->key[i]; //把父结点上大于该关键字的关键字移下来被删关键字所在的结点 98 99 father->key[i] = leftBrother->key[leftBrother->keynum]; //从左兄弟借关键字,把左兄弟的最大关键字上移到父结点 100 leftBrother->keynum -= 1; //左兄弟关键字个数减1(由于是借了左兄弟的最大关键字,所以不需要对关键字数组及子树数组作处理) 101 p->keynum += 1; //右兄弟关键字个数加1 102 } 103 104 else if (NULL != rightBrother&&rightBrother->keynum > ((m + 1) / 2 - 1)) //右兄弟有剩余关键字 105 { 106 //被删关键字所在的结点的处理,从父结点借关键字 107 p->key[p->keynum + 1] = father->key[i+1]; 108 p->ptr[p->keynum + 1] = rightBrother->ptr[0]; //子树指针指向右兄弟最左边的子树指针 109 110 if(NULL!=p->ptr[p->keynum + 1]) 111 p->ptr[p->keynum + 1]->parent = p; //新借来的结点把其父指针指向自己 112 113 p->keynum += 1; 114 115 father->key[i + 1] = rightBrother->key[1]; //父结点从右兄弟借关键字 116 117 for (int j = 0;j < rightBrother->keynum;j++) //右兄弟失去第一个关键字包括子树指针 118 { 119 if (j > 0) 120 rightBrother->key[j] = rightBrother->key[j + 1]; 121 rightBrother->ptr[j] = rightBrother->ptr[j + 1]; 122 } 123 124 rightBrother->ptr[rightBrother->keynum] = NULL; //数据前移一位之后最后一个子树指针指向空 125 126 rightBrother->keynum -= 1; //右兄弟关键字数减一 127 } 128 129 else //假如左右子树均没富余关键字,则需要合并 130 { 131 if (NULL != leftBrother) //假如左子树存在,与左子树合并 132 { 133 leftBrother->key[leftBrother->keynum + 1] = father->key[i]; //从父结点拿下分割本节点与左兄弟的关键字 134 leftBrother->ptr[leftBrother->keynum + 1] = p->ptr[0]; //把本节点的第一个子树指针放到左兄弟中去 135 136 if(NULL!= leftBrother->ptr[leftBrother->keynum + 1]) 137 leftBrother->ptr[leftBrother->keynum + 1]->parent = leftBrother; //给左兄弟的结点,当此结点存在时需要把其父亲指向指向左结点 138 139 leftBrother->keynum += 1; //左兄弟关键数加1 140 for (int j = 1;j <= p->keynum;j++) //把本结点的关键字和子树指针赋给左孩子 141 { 142 leftBrother->key[leftBrother->keynum + j] = p->key[j]; 143 leftBrother->ptr[leftBrother->keynum + j] = p->ptr[j]; 144 if(NULL!= leftBrother->ptr[leftBrother->keynum + j]) 145 leftBrother->ptr[leftBrother->keynum + j]->parent = leftBrother; //给左兄弟的结点,需要把其父亲指向指向左结点 146 } 147 leftBrother->keynum += p->keynum; //左孩子的关键字个数变为原个数加本结点的个数(上面借父结点的关键字时已加1) 148 149 father->ptr[i] = NULL; //p已经不再需要,所以把父结点的这个子树指针指为空 150 151 free(p); //p结点在把所有关键字及子树指针赋值出去之后释放掉 152 153 for (int j = i;j < father->keynum;j++) //父结点把要删除的孩子结点删除 154 { 155 father->key[j] = father->key[j + 1]; 156 father->ptr[j] = father->ptr[j + 1]; 157 } 158 159 father->ptr[father->keynum] = NULL; //数据前移之后最后的子树指向为空 160 161 father->keynum -= 1; //父结点关键字个数减1 162 163 if (root == father) //如果此时父结点为根,则当父结点没有关键字时才调整 164 { 165 if (0 == father->keynum) 166 { 167 for (int j = 0;j <= father->keynum + 1;j++) 168 if (father->ptr[j] != NULL) 169 { 170 root = father->ptr[j]; 171 break; 172 } 173 root->parent = NULL; 174 } 175 } 176 else //如果父结点不为根,则需要判断 177 { 178 if (father->keynum < (m - 1) / 2) 179 Restore(root, father); //因为父结点关键字少了1,所以要对父结点进行判断是否需要调整 180 } 181 182 } 183 184 else if(rightBrother!=NULL) //否则与右子树合并(根据B树性质,除了根节点左右兄弟必定存在一个) 185 { 186 for (int j = (rightBrother->keynum);j >= 0;j--) //把右兄弟所有的关键字及子树指针往后移1 + p->keynum个位置 187 { 188 if (j > 0) 189 rightBrother->key[j + 1 + p->keynum] = rightBrother->key[j]; 190 rightBrother->ptr[j + 1 + p->keynum] = rightBrother->ptr[j]; 191 } 192 193 rightBrother->key[p->keynum + 1] = father->key[i + 1]; //把父结点的分割两个本兄弟和右兄弟的关键字拿下来使用 194 195 for (int j = 0;j <= p->keynum;j++) //把本结点的关键字及子树指针移动右兄弟中去 196 { 197 if (j > 0) 198 rightBrother->key[j] = p->key[j]; 199 rightBrother->ptr[j] = p->ptr[j]; 200 if(NULL!= rightBrother->ptr[j]) 201 rightBrother->ptr[j]->parent = rightBrother; //给右兄弟的结点需要把其父结点指向右兄弟 202 } 203 rightBrother->keynum += (p->keynum + 1); //右兄弟的关键字数加上p->keynum + 1 204 205 father->ptr[i] = NULL; //结点数据移出去后把父亲指在该结点上的子树改为空 206 207 free(p); //p结点在把所有关键字及子树指针赋值出去之后释放掉 208 209 for (int j = i ;j < father->keynum;j++) //删除分割的关键字 210 { 211 if (j > i) 212 father->key[j] = father->key[j + 1]; 213 father->ptr[j] = father->ptr[j + 1]; 214 } 215 216 if (1 == father->keynum) //如果父结点在关键字减少之前只有一个结点,那么需要把父结点的右孩子赋值给左孩子 217 father->ptr[0] = father->ptr[1]; 218 219 father->ptr[father->keynum] = NULL; 220 221 father->keynum -= 1; //父结点关键字数减1 222 223 //if (father->keynum < (m - 1) / 2) //判断是否需要调整 224 //Restore(root,father); 225 226 if (root == father) //如果此时父结点为根,则当父结点没有关键字时才调整 227 { 228 if (0 == father->keynum) 229 { 230 for (int j = 0;j <= father->keynum + 1;j++) 231 if (father->ptr[j] != NULL) 232 { 233 root = father->ptr[j]; 234 break; 235 } 236 root->parent = NULL; 237 } 238 } 239 else //如果父结点不为根,则需要判断 240 { 241 if (father->keynum < (m - 1) / 2) 242 Restore(root, father); //因为父结点关键字少了1,所以要对父结点进行判断是否需要调整 243 } 244 } 245 else //当左右子树不存在时改变根结点 246 { 247 for (int j = 0;j <= p->keynum+1;j++) 248 if (p->ptr[j] != NULL) 249 { 250 root = p->ptr[j]; 251 break; 252 } 253 root->parent = NULL; 254 255 } 256 } 257 } 258 259 else //根节点,去掉根节点,使树减一层 260 { 261 BTree a; 262 for (int j=0;j <= p->keynum+1;j++) 263 { 264 if (p->ptr[j] != NULL) 265 { 266 a = p; 267 p = p->ptr[j]; 268 a->ptr[j] = NULL; 269 free(a); 270 break; 271 } 272 } 273 root = p; //因为此时根结点已经改变,所以需要把根结点改回来 274 root->parent=NULL; 275 } 276 }
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//顺序遍历
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
实现思路:
由于B树中关键字的存储有一定的升序结构,所以可以顺序遍历B。从根结点开始,使用循环,循环内含递归,在每个结点中,
先访问第一个子树指针,然后输出该指针后的一个关键字,再访问下一个指针,输出下一个关键字,依此类推,
使用循环递归的方式顺序输出所有的关键字。
实现代码:
//-------------------------------------------------------------------------------------- //函数名...............:OrderTraverseTree(BTree p) //功能.................:遍历B树 //-------------------------------------------------------------------------------------- void OrderTraverseTree(BTree p) { int i,key=0; if (NULL == p) return ; for (i = 0;i <= p->keynum;i++) { if (i > 0) cout << p->key[i] << " " ; if(p->ptr[i]!=NULL) OrderTraverseTree(p->ptr[i]); } }
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//层次遍历
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
实现思路:
为了更加直观地观察到树的结构,所以使用一种层次遍历的方式输出所有关键字。在这里不使用循环的方式,使用队列的思想,
在C++中借助容器vector实现一个队列。从根结点开始,循环访问子树和关键字,每遇到一个子树指针,则把它压入容器的后面,
遇到关键字则把它输出。处理完一个结点后从容器头部弹出一个指针并获得这个指针,然后用同样的方式访问这个指针。
在这个过程中为了显示出树的层次性,借助了三个变量。分别是上一层结点所拥有的总子树指针、这一层结点所拥有的总子树指针、
当前已经处理的子树指针个数。当当前所处理的指针个数等于上一层结点所拥有的总子树指针个数则表示当前层已经处理完,
换行后再处理下一层。同时,使上一层子树指针个数等于这一层子树指针个数,这一层子树指针个数变为0,当前已处理的指针个数变为0,
然后开始下一层的处理。这样一来,可以一层一层地输出树中的所有数据,显示出树的层次性,并且在调试过程中有助于找出问题。
实现代码:
1 //-------------------------------------------------------------------------------------- 2 //函数名...............:LevelOrderTraverseTree(BTree p) 3 //功能.................:层次遍历B树 4 //-------------------------------------------------------------------------------------- 5 void LevelOrderTraverseTree(BTree p) 6 { 7 deque<BTree> TreeDeque; 8 BTree q; 9 int lastlevelnum,thislevelnum, each=0; //分别代表上一层有几个子树,本层有几个子树,当前处理了几个子树 10 thislevelnum = 0; 11 12 if (NULL == p) 13 return; 14 15 cout << "["; //以左中括号为每个结点的开始 16 cout << p->keynum << " "; 17 for (int i = 0;i <= p->keynum;i++) 18 { 19 if (i > 0) 20 cout << p->key[i] << " " ; 21 if (p->ptr[i] != NULL) //把遇到的每个子树指针放到容器中 22 TreeDeque.push_back(p->ptr[i]); 23 } 24 cout << "]"<<endl; //以右中括号为每个结点的结束 25 lastlevelnum = (p->keynum+1); //此时上一层子树数为这个 26 27 while (!TreeDeque.empty()) 28 { 29 q = TreeDeque[0]; 30 thislevelnum += (q->keynum+1); //这一层的子树为这层每个结点的子树数相加 31 cout << "["; 32 cout << q->keynum << " "; 33 34 for (int i = 0;i <= q->keynum;i++) 35 { 36 if (i > 0) 37 cout << q->key[i] << " "; 38 if (q->ptr[i] != NULL) 39 TreeDeque.push_back(q->ptr[i]); //把遇到的子树放到容器中 40 } 41 cout << "]"; 42 each++; //当前处理过的子树数加1 43 44 if (each == lastlevelnum) //当处理当前的子树数等于上一层的子树数时代表这一层已处理完毕 45 { 46 cout << endl; 47 each = 0; //这一层的子树数改为0 48 lastlevelnum = thislevelnum; //上一层的子树数变为这一层的 49 thislevelnum = 0; //这一层的变为0,重新开始 50 } 51 TreeDeque.pop_front(); //处理完后把子树指针弹出 52 } 53 }
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//主函数
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
1 //-------------------------------------------------------------------------------------- 2 //文件名.......: main.cpp 3 //功能.........: 调用前面的函数实现功能 4 //-------------------------------------------------------------------------------------- 5 6 //-------------------------------------------------------------------------------------- 7 //加入头文件以及使用命名空间 8 //-------------------------------------------------------------------------------------- 9 #include"BTree.h" 10 #include<ctime> 11 #include<cstdlib> 12 13 int main() 14 { 15 BTree p,tree; 16 result re; 17 int num; 18 int a[30] = { 20,11,22,13,26,2,28,18,21,16,23,3,24,12,27,0,29,19,4,14,5,1,8,17,7,25,9,10,6 ,15}; 19 Init(p); 20 Init(tree); 21 22 cout << "........................................................................................." << endl; 23 cout << ". 树的阶数为:"<<m<<" ." << endl; 24 cout << ". 现在要生成一棵新B树,请输入数据个数 ." << endl; 25 cout << ". ." << endl; 26 cout << ". ." << endl; 27 cout << ". ." << endl; 28 cout << "........................................................................................." << endl; 29 cout << "数据个数:"; 30 cin >> num; 31 32 //根据输入随机生成数据然后插入 33 for (int i = 0;i < num;i++) 34 { 35 int data; 36 srand(i); 37 data = rand() % (num * 2 + 1); 38 SearchBTree(tree, data, re); 39 InsertBTree(tree, data, re.pt, re.i); 40 } 41 cout << "树生成成功,下面按层次以及顺序的方式输出该树" << endl; 42 LevelOrderTraverseTree(tree); 43 OrderTraverseTree(tree); 44 cout << endl; 45 //循环选择操作 46 while (1) 47 { 48 int choic; 49 int newData; 50 int deleteDate; 51 cout << "........................................................................................." << endl; 52 cout << ". 树的阶数为:" << m << " ." << endl; 53 cout << ". 请选择操作 ." << endl; 54 cout << ". 1、插入数据 ." << endl; 55 cout << ". 2、删除数据 ." << endl; 56 cout << ". 3、顺序输出一棵树 ." << endl; 57 cout << ". 4、层次输出一棵树 ." << endl; 58 cout << ". 5、演示整棵树的删除 ." << endl; 59 cout << ". 6、退出 ." << endl; 60 cout << "........................................................................................." << endl; 61 cout << "请选择操作:"; 62 cin >> choic; 63 64 switch (choic) 65 { 66 case 1: //插入操作 67 cout << "请输入想要插入的数据:"; 68 cin >> newData; 69 cout << endl; 70 SearchBTree(tree, newData, re); 71 if (0 == re.tag) 72 { 73 InsertBTree(tree, newData, re.pt, re.i); 74 cout << "插入成功,下面按层次以及顺序的方式输出新树" << endl; 75 LevelOrderTraverseTree(tree); 76 OrderTraverseTree(tree); 77 cout << endl; 78 } 79 else 80 { 81 cout << "数据输入有误,该数据已存在" << endl; 82 } 83 break; 84 85 case 2: //删除操作 86 cout << "请输入想要删除的数据:"; 87 cin >> deleteDate; 88 SearchBTree(tree, deleteDate, re); 89 if (1 == re.tag) 90 { 91 DeleteBTree(tree, re.pt, re.i); 92 cout << "数据删除成功,下面按层次以及顺序的方式输出新树" << endl; 93 LevelOrderTraverseTree(tree); 94 OrderTraverseTree(tree); 95 cout << endl; 96 } 97 else 98 { 99 cout << "数据输入有误,该数据不存在" << endl; 100 } 101 break; 102 103 case 3: //顺序输出操作 104 OrderTraverseTree(tree); 105 cout << endl; 106 break; 107 108 case 4: //层次输出操作 109 LevelOrderTraverseTree(tree); 110 break; 111 112 case 5: //演示全部删除一棵树 113 //先生成一棵固定的树 114 for (int i = 0;i < 30;i++) 115 { 116 SearchBTree(p, a[i], re); 117 InsertBTree(p, a[i], re.pt, re.i); 118 } 119 120 cout << "一棵新的树如下,按任意键开始演示全部删除" << endl; 121 LevelOrderTraverseTree(p); 122 OrderTraverseTree(p); 123 cout << endl; 124 system("pause"); 125 //全部删除这棵固定的树 126 for (int i = 0;i < 30;i++) 127 { 128 SearchBTree(p, a[i], re); 129 DeleteBTree(p, re.pt, re.i); 130 LevelOrderTraverseTree(p); 131 OrderTraverseTree(p); 132 cout << endl; 133 cout << endl; 134 } 135 cout << endl; 136 break; 137 138 case 6:exit(0); //退出 139 } 140 } 141 142 system("pause"); 143 }
运行结果: