B树完整实现

-B树完整接口实现

B树的定义
B树是一种平衡的多路查找树。
一颗m阶B树,或为空树,或为满足下列特性的m叉树。
(1)树中每个结点最多含有m棵子树;
(2)若根结点不是叶子结点,则至少有两颗子树;
(3)除根之外的所有非终端结点至少有[m/2];
(4)每个非终端结点中包含信息:(n,A0,K1,A1,K2,A2,…,Kn,An)。其中
①Ki(1≤i≤n)为关键字,且关键字按升序排序。
②指针Ai(0≤i≤n)指向子树的根结点。
③关键字的个数n必须满足:[m/2]-1≤n≤m-1
(5)所有的叶子结点都出现在同一层次上,并且不带信息(可以看作是外部节点或查找失败的结点,实际上这些结点不存在,指向这些结点的指针为空)

编程环境与配置
IDE:Dev-C++ 5.11
编程语言:C

程序结构图
这里写图片描述
B树的抽象数据类型定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
ADT BTree{
数据对象:D是具有相同特性的数据元素的集合
数据关系:R1={<ai-1,ai>|ai-1,ai∈D,i=2,...,n}
R2={<ptr[i-1],ptr[i]>|i=1...,n}
约定a1|key[1]为关键字数组头,an|key[p-<keynum]为关键字数组尾
约定ptr[i]为结点的第i个子树
基本操作:
InitBTree(t)
初始条件:B树已定义
操作结果:初始化B树
SearchBTNode(BTNode *p,KeyType k)
初始条件:结点p已存在
操作结果:在结点p中查找关键字k的插入位置i
Result SearchBTree(BTree t,KeyType k)
初始条件:B树已存在
操作结果:在B树查找关键字k的插入位置,返回查找结果
InsertBTNode(BTNode *&p,int i,KeyType k,BTNode *q)
初始条件:结点p和结点q已存在,0<i<p->keynum
操作结果:将关键字k和结点q分别插入到p->key[i+1]和p->ptr[i+1]中
SplitBTNode(BTNode *&p,BTNode *&q)
初始条件:结点p和结点q已存在
操作结果:将结点p分裂成两个结点,前一半保留,后一半移入结点q
NewRoot(BTNode *&t,KeyType k,BTNode *p,BTNode *q)
初始条件:结点t,p,q已存在
操作结果:生成新的根结点t,原p和q为子树指针
InsertBTree(BTree &t,int i,KeyType k,BTNode *p)
初始条件:结点p和结点t已存在,0<i<p->keynum
操作结果:在B树t中插入关键字k
Remove(BTNode *p,int i)
初始条件:结点p已存在,0<i<p->keynum
操作结果:p结点删除key[i]和它的孩子指针ptr[i]
Substitution(BTNode *p,int i)
初始条件:结点p已存在,0<i<p->keynum
操作结果:查找替代值
MoveRight(BTNode *p,int i)
初始条件:结点p已存在,0<i<p->keynum
操作结果:结点调整右移操作
MoveLeft(BTNode *p,int i)
初始条件:结点p已存在,0<i<p->keynum
操作结果:结点调整左移操作
Combine(BTNode *p,int i)
初始条件:结点p已存在,0<i<p->keynum
操作结果:结点调整合并操作
AdjustBTree(BTNode *p,int i)
初始条件:结点p已存在,0<i<p->keynum
操作结果:B树调整操作
BTNodeDelete(BTNode *p,KeyType k)
初始条件:结点p已存在
操作结果:在结点p中删除关键字k
BTreeDelete(BTree &t,KeyType k)
初始条件:B树t已存在
操作结果:在B树t中删除关键字k
DestroyBTree(BTree &t)
初始条件:B树t已存在
操作结果:递归释放B树
PrintBTree(BTree t)
初始条件:B树t已存在
操作结果:遍历打印B树
}ADT BTree

头文件
定义了需要用到的数据类型,结构体类型,以及所有函数接口;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//==========ADT BTree的表示与实现==========
#ifndef _BTREE_H
#define _BTREE_H
#define MAXM 10 //定义B树的最大的阶数
const int m=4; //设定B树的阶数
const int Max=m-1; //结点的最大关键字数量
const int Min=(m-1)/2; //结点的最小关键字数量
typedef int KeyType; //KeyType为关键字类型
//===============B树存储结构==============
typedef struct node{ //B树和B树结点类型
int keynum; //结点关键字个数
KeyType key[MAXM]; //关键字数组,key[0]不使用
struct node *parent; //双亲结点指针
struct node *ptr[MAXM]; //孩子结点指针数组
}BTNode,*BTree;
typedef struct{ //B树查找结果类型
BTNode *pt; //指向找到的结点
int i; //在结点中的关键字位置;
int tag; //查找成功与否标志
}Result;
typedef struct LNode{ //链表和链表结点类型
BTree data; //数据域
struct LNode *next; //指针域
}LNode, *LinkList;
typedef enum status{ //枚举类型(依次递增)
TRUE,
FALSE,
OK,
ERROR,
OVERFLOW,
EMPTY
}Status;
//============基本操作的函数原型声明=============
Status InitBTree(BTree &t);
//初始化B树
int SearchBTNode(BTNode *p,KeyType k);
//在结点p中查找关键字k的插入位置i
Result SearchBTree(BTree t,KeyType k);
/*在树t上查找关键字k,返回结果(pt,i,tag)。若查找成功,则特征值
tag=1,关键字k是指针pt所指结点中第i个关键字;否则特征值tag=0,
关键字k的插入位置为pt结点的第i个*/
void InsertBTNode(BTNode *&p,int i,KeyType k,BTNode *q);
//将关键字k和结点q分别插入到p->key[i+1]和p->ptr[i+1]中
void SplitBTNode(BTNode *&p,BTNode *&q);
//将结点p分裂成两个结点,前一半保留,后一半移入结点q
void NewRoot(BTNode *&t,KeyType k,BTNode *p,BTNode *q);
//生成新的根结点t,原结点p和结点q为子树指针
void InsertBTree(BTree &t,int i,KeyType k,BTNode *p);
/*在树t上结点q的key[i]与key[i+1]之间插入关键字k。若引起
结点过大,则沿双亲链进行必要的结点分裂调整,使t仍是B树*/
void Remove(BTNode *p,int i);
//从p结点删除key[i]和它的孩子指针ptr[i]
void Substitution(BTNode *p,int i);
//查找被删关键字p->key[i](在非叶子结点中)的替代叶子结点(右子树中值最小的关键字)
void MoveRight(BTNode *p,int i);
/*将双亲结点p中的最后一个关键字移入右结点q中
将左结点aq中的最后一个关键字移入双亲结点p中*/
void MoveLeft(BTNode *p,int i);
/*将双亲结点p中的第一个关键字移入结点aq中,
将结点q中的第一个关键字移入双亲结点p中*/
void Combine(BTNode *p,int i);
/*将双亲结点p、右结点q合并入左结点aq,
并调整双亲结点p中的剩余关键字的位置*/
void AdjustBTree(BTNode *p,int i);
//删除结点p中的第i个关键字后,调整B树
int FindBTNode(BTNode *p,KeyType k,int &i);
//反映是否在结点p中是否查找到关键字k
int BTNodeDelete(BTNode *p,KeyType k);
//在结点p中查找并删除关键字k
void BTreeDelete(BTree &t,KeyType k);
//构建删除框架,执行删除操作
void DestroyBTree(BTree &t);
//递归释放B树
Status InitQueue(LinkList &L);
//初始化队列
LNode* CreateNode(BTree t);
//新建一个结点
Status Enqueue(LNode *p,BTree t);
//元素q入队列
Status Dequeue(LNode *p,BTNode *&q);
//出队列,并以q返回值
Status IfEmpty(LinkList L);
//队列判空
void DestroyQueue(LinkList L);
//销毁队列
Status Traverse(BTree t,LinkList L,int newline,int sum);
//用队列遍历输出B树
Status PrintBTree(BTree t);
//输出B树
void Test();
//测试B树功能函数
#endif

B树具体接口实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
2.4.1InitBTree函数
功能:初始化B树
代码实现:
Status InitBTree(BTree &t){
t=NULL;
return OK;
}
2.4.2SearchBTNode函数
功能:在结点p中查找关键字k的插入位置i
代码实现:
int SearchBTNode(BTNode *p,KeyType k){
int i=0;
for(i=0;i<p->keynum&&p->key[i+1]<=k;i++);
return i;
}
2.4.3SearchBTree函数
功能:在树t中查找关键字k,返回查找结果类型
代码实现:
Result SearchBTree(BTree t,KeyType k){
BTNode *p=t,*q=NULL; //初始化结点p和结点q,p指向待查结点,q指向p的双亲
int found_tag=0; //设定查找成功与否标志
int i=0;
Result r; //设定返回的查找结果
while(p!=NULL&&found_tag==0){
i=SearchBTNode(p,k); //在结点p中查找关键字k if(i>0&&p->key[i]==k) //找到待查关键字
found_tag=1; //查找成功
else{ //查找失败
q=p;
p=p->ptr[i];
}
}
if(found_tag==1){ //查找成功
r.pt=p;
r.i=i;
r.tag=1;
}
else{ //查找失败
r.pt=q;
r.i=i;
r.tag=0;
}
return r; //返回关键字k的位置(或插入位置)
}
2.4.4InsertBTNode函数
功能:关键字k和结点q分别插入到p->key[i+1]和p->ptr[i+1]中
代码实现:
void InsertBTNode(BTNode *&p,int i,KeyType k,BTNode *q){
int j;
for(j=p->keynum;j>i;j--){ //整体后移空出一个位置
p->key[j+1]=p->key[j];
p->ptr[j+1]=p->ptr[j];
}
p->key[i+1]=k;
p->ptr[i+1]=q;
if(q!=NULL)
q->parent=p;
p->keynum++;
}
2.4.5SplitBTNode函数
功能:将结点p分裂成两个结点,前一半保留,后一半移入结点q
代码实现:
void SplitBTNode(BTNode *&p,BTNode *&q){
int i;
int s=(m+1)/2;
q=(BTNode *)malloc(sizeof(BTNode)); //给结点q分配空间
q->ptr[0]=p->ptr[s]; //后一半移入结点q
for(i=s+1;i<=m;i++){
q->key[i-s]=p->key[i];
q->ptr[i-s]=p->ptr[i];
}
q->keynum=p->keynum-s;
q->parent=p->parent;
for(i=0;i<=p->keynum-s;i++) //修改双亲指针
if(q->ptr[i]!=NULL)
q->ptr[i]->parent=q;
p->keynum=s-1; //结点p的前一半保留,修改结点p的keynum
}
2.4.6NewRoot函数
功能:生成新的根结点t,原p和q为子树指针
代码实现:
void NewRoot(BTNode *&t,KeyType k,BTNode *p,BTNode *q){
t=(BTNode *)malloc(sizeof(BTNode)); //分配空间
t->keynum=1;
t->ptr[0]=p;
t->ptr[1]=q;
t->key[1]=k;
if(p!=NULL) //调整结点p和q的双亲指针
p->parent=t;
if(q!=NULL)
q->parent=t;
t->parent=NULL;
}
2.4.7InsertBTree函数
功能:在树t中插入关键字k,返回插入结果
代码实现:
void InsertBTree(BTree &t,int i,KeyType k,BTNode *p){
BTNode *q;
int finish_tag,newroot_tag,s; //设定需要新结点标志和插入完成标志
KeyType x;
if(p==NULL) //t是空树
NewRoot(t,k,NULL,NULL); //生成仅含关键字k的根结点t
else{
x=k;
q=NULL;
finish_tag=0;
newroot_tag=0;
while(finish_tag==0&&newroot_tag==0){
InsertBTNode(p,i,x,q); //将关键字x和结点q分别插入到p->key[i+1]和p->ptr[i+1]
if (p->keynum<=Max)
finish_tag=1; //插入完成
else{
s=(m+1)/2;
SplitBTNode(p,q); //分裂结点
x=p->key[s];
if(p->parent){ //查找x的插入位置
p=p->parent;
i=SearchBTNode(p, x);
}
else //没找到x,需要新结点
newroot_tag=1;
}
}
if(newroot_tag==1) //根结点已分裂为结点p和q
NewRoot(t,x,p,q); //生成新根结点t,p和q为子树指针
}
}
2.4.8Remove函数
功能:从p结点删除key[i]和它的孩子指针ptr[i]
代码实现:
void Remove(BTNode *p,int i){
int j;
for(j=i+1;j<=p->keynum;j++){ //前移删除key[i]和ptr[i]
p->key[j-1]=p->key[j];
p->ptr[j-1]=p->ptr[j];
}
p->keynum--;
}
2.4.9Substitution函数
功能:寻找替代值(右子树中最小的关键字)
代码实现:
void Substitution(BTNode *p,int i){
BTNode *q;
for(q=p->ptr[i];q->ptr[0]!=NULL;q=q->ptr[0]);
p->key[i]=q->key[1]; //复制关键字值
}
2.4.10MoveRight函数
功能:双亲结点p中的最后一个关键字移入右结点q中
将左结点aq中的最后一个关键字移入双亲结点p中
代码实现:
void MoveRight(BTNode *p,int i){
int j;
BTNode *q=p->ptr[i];
BTNode *aq=p->ptr[i-1];
for(j=q->keynum;j>0;j--){ //将右兄弟q中所有关键字向后移动一位
q->key[j+1]=q->key[j];
q->ptr[j+1]=q->ptr[j];
}
q->ptr[1]=q->ptr[0]; //从双亲结点p移动关键字到右兄弟q中
q->key[1]=p->key[i];
q->keynum++;
p->key[i]=aq->key[aq->keynum]; //将左兄弟aq中最后一个关键字移动到双亲结点p中
p->ptr[i]->ptr[0]=aq->ptr[aq->keynum];
aq->keynum--;
}
2.4.11MoveLeft函数
功能:将双亲结点p中的第一个关键字移入左结点aq中,
将右结点q中的第一个关键字移入双亲结点p中
代码实现:
void MoveLeft(BTNode *p,int i){
int j;
BTNode *aq=p->ptr[i-1];
BTNode *q=p->ptr[i];
aq->keynum++; //把双亲结点p中的关键字移动到左兄弟aq中
aq->key[aq->keynum]=p->key[i];
aq->ptr[aq->keynum]=p->ptr[i]->ptr[0];
p->key[i]=q->key[1]; //把右兄弟q中的关键字移动到双亲节点p中
q->ptr[0]=q->ptr[1];
q->keynum--;
for(j=1;j<=aq->keynum;j++){ //将右兄弟q中所有关键字向前移动一位
aq->key[j]=aq->key[j+1];
aq->ptr[j]=aq->ptr[j+1];
}
}
2.4.12Combine函数
功能:双亲结点p、右结点q合并入左结点aq,
并调整双亲结点p中的剩余关键字的位置
代码实现:
void Combine(BTNode *p,int i){
int j;
BTNode *q=p->ptr[i];
BTNode *aq=p->ptr[i-1];
aq->keynum++; //将双亲结点的关键字p->key[i]插入到左结点aq
aq->key[aq->keynum]=p->key[i];
aq->ptr[aq->keynum]=q->ptr[0];
for(j=1;j<=q->keynum;j++){ //将右结点q中的所有关键字插入到左结点aq
aq->keynum++;
aq->key[aq->keynum]=q->key[j];
aq->ptr[aq->keynum]=q->ptr[j];
}
for(j=i;j<p->keynum;j++){ //将双亲结点p中的p->key[i]后的所有关键字向前移动一位
p->key[j]=p->key[j+1];
p->ptr[j]=p->ptr[j+1];
}
p->keynum--; //修改双亲结点p的keynum值
free(q); //释放空右结点q的空间
}
2.4.13AdjustBTree函数
功能:删除结点p中的第i个关键字后,调整B树
代码实现:
void AdjustBTree(BTNode *p,int i){
if(i==0) //删除的是最左边关键字
if(p->ptr[1]->keynum>Min) //右结点可以借
MoveLeft(p,1);
else //右兄弟不够借
Combine(p,1);
else if(i==p->keynum) //删除的是最右边关键字
if(p->ptr[i-1]->keynum>Min) //左结点可以借
MoveRight(p,i);
else //左结点不够借
Combine(p,i);
else if(p->ptr[i-1]->keynum>Min) //删除关键字在中部且左结点够借
MoveRight(p,i);
else if(p->ptr[i+1]->keynum>Min) //删除关键字在中部且右结点够借
MoveLeft(p,i+1);
else //删除关键字在中部且左右结点都不够借
Combine(p,i);
}
2.4.14BTNodeDelete函数
功能:在结点p中查找并删除关键字k
代码实现:
int BTNodeDelete(BTNode *p,KeyType k){
int i;
int found_tag; //查找标志
if(p==NULL)
return 0;
else{
found_tag=FindBTNode(p,k,i); //返回查找结果
if(found_tag==1){ //查找成功
if(p->ptr[i-1]!=NULL){ //删除的是非叶子结点
Substitution(p,i); //寻找相邻关键字(右子树中最小的关键字)
BTNodeDelete(p->ptr[i],p->key[i]); //执行删除操作
}
else
Remove(p,i); //从结点p中位置i处删除关键字
}
else
found_tag=BTNodeDelete(p->ptr[i],k); //沿孩子结点递归查找并删除关键字k
if(p->ptr[i]!=NULL)
if(p->ptr[i]->keynum<Min) //删除后关键字个数小于MIN
AdjustBTree(p,i); //调整B树
return found_tag;
}
}
2.4.15BTreeDelete函数
功能:构建删除框架,执行删除操作
代码实现:
void BTreeDelete(BTree &t,KeyType k){
BTNode *p;
int a=BTNodeDelete(t,k); //删除关键字k
if(a==0) //查找失败
printf(" 关键字%d不在B树中\n",k);
else if(t->keynum==0){ //调整
p=t;
t=t->ptr[0];
free(p);
}
}
2.4.16DestroyBTree函数
功能:递归释放B树
代码实现:
void DestroyBTree(BTree &t){
int i;
BTNode* p=t;
if(p!=NULL){ //B树不为空
for(i=0;i<=p->keynum;i++){ //递归释放每一个结点
DestroyBTree(*&p->ptr[i]);
}
free(p);
}
t=NULL;
}
2.4.17InitQueue函数
功能:初始化队列
代码实现:
Status InitQueue(LinkList &L){
L=(LNode*)malloc(sizeof(LNode)); //分配结点空间
if(L==NULL) //分配失败
return OVERFLOW;
L->next=NULL;
return OK;
}
2.4.18CreateNode函数
功能:新建一个结点
代码实现:
LNode* CreateNode(BTNode *p){
LNode *q;
q=(LNode*)malloc(sizeof(LNode)); //分配结点空间
if(q!=NULL){ //分配成功
q->data=p;
q->next=NULL;
}
return q;
}
2.4.19Enqueue函数
功能:元素q入队列
代码实现:
Status Enqueue(LNode *p,BTNode *q){
if(p==NULL)
return ERROR;
while(p->next!=NULL) //调至队列最后
p=p->next;
p->next=CreateNode(q); //生成结点让q进入队列
return OK;
}
2.4.20Dequeue函数
功能:出队列,并以q返回值
代码实现:
Status Dequeue(LNode *p,BTNode *&q){
LNode *aq;
if(p==NULL||p->next==NULL) //删除位置不合理
return ERROR;
aq=p->next; //修改被删结点aq的指针域
p->next=aq->next;
q=aq->data;
free(aq); //释放结点aq
return OK;
}
2.4.21IfEmpty函数
功能:队列判空
代码实现:
Status IfEmpty(LinkList L){
if(L==NULL) //队列不存在
return ERROR;
if(L->next==NULL) //队列为空
return TRUE;
return FALSE; //队列非空
}
2.4.22DestroyQueue函数
功能:销毁队列
代码实现:
void DestroyQueue(LinkList L){
LinkList p;
if(L!=NULL){
p=L;
L=L->next;
free(p); //逐一释放
DestroyQueue(L);
}
}
2.4.23Traverse函数
功能:用队列遍历输出B树
代码实现:
Status Traverse(BTree t,LinkList L,int newline,int sum){
int i;
BTree p;
if(t!=NULL){
printf(" [ ");
Enqueue(L,t->ptr[0]); //入队
for(i=1;i<=t->keynum;i++){
printf(" %d ",t->key[i]);
Enqueue(L,t->ptr[i]); //子结点入队
}
sum+=t->keynum+1;
printf("]");
if(newline==0){ //需要另起一行
printf("\n");
newline=sum-1;
sum=0;
}
else
newline--;
}
if(IfEmpty(L)==FALSE){ //l不为空
Dequeue(L,p); //出队,以p返回
Traverse(p,L,newline,sum); //遍历出队结点
}
return OK;
}
2.4.24PrintBTree函数
功能:输出B树
代码实现:
Status PrintBTree(BTree t){
LinkList L;
if(t==NULL){
printf(" B树为空树");
return OK;
}
InitQueue(L); //初始化队列
Traverse(t,L,0,0); //利用队列输出
DestroyQueue(L); //销毁队列
return OK;
}
2.4.25Test1函数
功能:测试B树功能
代码实现:
void Test1(){
system("color 70");
BTNode *t=NULL;
Result s; //设定查找结果
int j,n=15;
KeyType k;
KeyType a[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
printf("创建一棵%d阶B树:\n",m);
for(j=0;j<n;j++){ //逐一插入元素
s=SearchBTree(t,a[j]);
if(s.tag==0)
InsertBTree(t,s.i,a[j],s.pt);
printf(" 第%d步,插入元素%d:\n ",j+1,a[j]);
PrintBTree(t);
}
printf("\n");
printf("删除操作:\n"); //删除操作
k=9;
BTreeDelete(t,k);
printf(" 删除%d:\n ",k);
printf(" 删除后的B树: \n");
PrintBTree(t);
printf("\n");
k=1;
BTreeDelete(t,k);
printf(" 删除%d:\n ",k);
printf(" 删除后的B树: \n");
PrintBTree(t);
printf("\n");
printf(" 递归释放B树\n"); //递归释放B树
DestroyBTree(t);
PrintBTree(t);
}
2.4.26Test2函数
功能:测试B树功能
代码实现:
void Test2(){
int i,k;
system("color 70");
BTree t=NULL;
Result s; //设定查找结果
while(1){
printf("此时的B树:\n");
PrintBTree(t);
printf("\n");
printf("=============Operation Table=============\n");
printf(" 1.Init 2.Insert 3.Delete \n");
printf(" 4.Destroy 5.Exit \n");
printf("=========================================\n");
printf("Enter number to choose operation:_____\b\b\b");
scanf("%d",&i);
switch(i){
case 1:{
InitBTree(t);
printf("InitBTree successfully.\n");
break;
}
case 2:{
printf("Enter number to InsertBTree:_____\b\b\b");
scanf("%d",&k);
s=SearchBTree(t,k);
InsertBTree(t,s.i,k,s.pt);
printf("InsertBTree successfully.\n");
break;
}
case 3:{
printf("Enter number to DeleteBTree:_____\b\b\b");
scanf("%d",&k);
BTreeDelete(t,k);
printf("\n");
printf("DeleteBTree successfully.\n");
break;
}
case 4:{
DestroyBTree(t);
break;
printf("DestroyBTree successfully.\n");
}
case 5:{
exit(-1);
break;
}
}
}
}

三.功能测试
插入功能测试/遍历功能测试
依次插入1-15进行测试输出,结果如下:
这里写图片描述
由输出的B树可知,插入功能正常并且遍历功能正常
3.2删除功能测试
在之前插入1-15后进行删除关键字的功能测试,选取9和1依次进行删除测试,结果如下:
这里写图片描述
根据B树的定义和该B树的输出,删除功能正常
3.3释放功能测试
在之前的基础上进行递归释放B树功能测试,结果如下:
这里写图片描述
遍历输出的结果为B数为空树,说明释放功能正常
3.4其他功能测试
其他接口在以上功能中已经有所体现,均正常,不再一一调用测试。
四.思考与小结
错误总结
(1)在部分需要判空的地方没有判空
(2)递归实现的时候多次爆栈
(3)插入分裂的SplitBTNode函数一开始写的时候分裂成两个
(4)删除操作中的Combine函数的指针忘记调整
4.2部分优化
4.2.1输出优化
在一开始输出的时候选用的是括号输出法(测试功能选用的值略有不同),在直观上比较难的去分辨哪些是双亲结点的左右结点,因此在输出函数上进行了优化
这里写图片描述
通过队列遍历,在每一次遍历的过程中能够,模拟层次遍历,在B树的结构上更加美观,而且更容易看清楚B树的结构
这里写图片描述
4.2.2测试界面优化
在保持原本接口不变的情况下,写了Test2函数,自行创建和进行各种B树的操作.
这里写图片描述

整个程序源代码
头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#ifndef _BTREE_H
#define _BTREE_H
#define MAXM 10 //定义B树的最大的阶数
const int m=4; //设定B树的阶数
const int Max=m-1; //结点的最大关键字数量
const int Min=(m-1)/2; //结点的最小关键字数量
typedef int KeyType; //KeyType为关键字类型
typedef struct node{ //B树和B树结点类型
int keynum; //结点关键字个数
KeyType key[MAXM]; //关键字数组,key[0]不使用
struct node *parent; //双亲结点指针
struct node *ptr[MAXM]; //孩子结点指针数组
}BTNode,*BTree;
typedef struct{ //B树查找结果类型
BTNode *pt; //指向找到的结点
int i; //在结点中的关键字位置;
int tag; //查找成功与否标志
}Result;
typedef struct LNode{ //链表和链表结点类型
BTree data; //数据域
struct LNode *next; //指针域
}LNode, *LinkList;
typedef enum status{ //枚举类型(依次递增)
TRUE,
FALSE,
OK,
ERROR,
OVERFLOW,
EMPTY
}Status;
Status InitBTree(BTree &t); //初始化B树
int SearchBTNode(BTNode *p,KeyType k); //在结点p中查找关键字k的插入位置i
Result SearchBTree(BTree t,KeyType k); /*在树t上查找关键字k,返回结果(pt,i,tag)。若查找成功,则特征值
tag=1,关键字k是指针pt所指结点中第i个关键字;否则特征值tag=0,
关键字k的插入位置为pt结点的第i个*/
void InsertBTNode(BTNode *&p,int i,KeyType k,BTNode *q); //将关键字k和结点q分别插入到p->key[i+1]和p->ptr[i+1]中
void SplitBTNode(BTNode *&p,BTNode *&q); //将结点p分裂成两个结点,前一半保留,后一半移入结点q
void NewRoot(BTNode *&t,KeyType k,BTNode *p,BTNode *q); //生成新的根结点t,原结点p和结点q为子树指针
void InsertBTree(BTree &t,int i,KeyType k,BTNode *p); /*在树t上结点q的key[i]与key[i+1]之间插入关键字k。若引起
结点过大,则沿双亲链进行必要的结点分裂调整,使t仍是B树*/
void Remove(BTNode *p,int i); //从p结点删除key[i]和它的孩子指针ptr[i]
void Substitution(BTNode *p,int i); //查找被删关键字p->key[i](在非叶子结点中)的替代叶子结点(右子树中值最小的关键字)
void MoveRight(BTNode *p,int i); /*将双亲结点p中的最后一个关键字移入右结点q中
将左结点aq中的最后一个关键字移入双亲结点p中*/
void MoveLeft(BTNode *p,int i); /*将双亲结点p中的第一个关键字移入结点aq中,
将结点q中的第一个关键字移入双亲结点p中*/
void Combine(BTNode *p,int i); /*将双亲结点p、右结点q合并入左结点aq,
并调整双亲结点p中的剩余关键字的位置*/
void AdjustBTree(BTNode *p,int i); //删除结点p中的第i个关键字后,调整B树
int FindBTNode(BTNode *p,KeyType k,int &i); //反映是否在结点p中是否查找到关键字k
int BTNodeDelete(BTNode *p,KeyType k); //在结点p中查找并删除关键字k
void BTreeDelete(BTree &t,KeyType k); //构建删除框架,执行删除操作
void DestroyBTree(BTree &t); //递归释放B树
Status InitQueue(LinkList &L); //初始化队列
LNode* CreateNode(BTree t); //新建一个结点
Status Enqueue(LNode *p,BTree t); //元素q入队列
Status Dequeue(LNode *p,BTNode *&q); //出队列,并以q返回值
Status IfEmpty(LinkList L); //队列判空
void DestroyQueue(LinkList L); //销毁队列
Status Traverse(BTree t,LinkList L,int newline,int sum); //用队列遍历输出B树
Status PrintBTree(BTree t); //输出B树
void Test(); //测试B树功能函数
#endif

BTree代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
#include"BTREE.h"
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
Status InitBTree(BTree &t){
//初始化B树
t=NULL;
return OK;
}
int SearchBTNode(BTNode *p,KeyType k){
//在结点p中查找关键字k的插入位置i
int i=0;
for(i=0;i<p->keynum&&p->key[i+1]<=k;i++);
return i;
}
Result SearchBTree(BTree t,KeyType k){
/*在树t上查找关键字k,返回结果(pt,i,tag)。若查找成功,则特征值
tag=1,关键字k是指针pt所指结点中第i个关键字;否则特征值tag=0,
关键字k的插入位置为pt结点的第i个*/
BTNode *p=t,*q=NULL; //初始化结点p和结点q,p指向待查结点,q指向p的双亲
int found_tag=0; //设定查找成功与否标志
int i=0;
Result r; //设定返回的查找结果
while(p!=NULL&&found_tag==0){
i=SearchBTNode(p,k); //在结点p中查找关键字k,使得p->key[i]<=k<p->key[i+1]
if(i>0&&p->key[i]==k) //找到待查关键字
found_tag=1; //查找成功
else{ //查找失败
q=p;
p=p->ptr[i];
}
}
if(found_tag==1){ //查找成功
r.pt=p;
r.i=i;
r.tag=1;
}
else{ //查找失败
r.pt=q;
r.i=i;
r.tag=0;
}
return r; //返回关键字k的位置(或插入位置)
}
void InsertBTNode(BTNode *&p,int i,KeyType k,BTNode *q){
//将关键字k和结点q分别插入到p->key[i+1]和p->ptr[i+1]中
int j;
for(j=p->keynum;j>i;j--){ //整体后移空出一个位置
p->key[j+1]=p->key[j];
p->ptr[j+1]=p->ptr[j];
}
p->key[i+1]=k;
p->ptr[i+1]=q;
if(q!=NULL)
q->parent=p;
p->keynum++;
}
void SplitBTNode(BTNode *&p,BTNode *&q){
//将结点p分裂成两个结点,前一半保留,后一半移入结点q
int i;
int s=(m+1)/2;
q=(BTNode *)malloc(sizeof(BTNode)); //给结点q分配空间
q->ptr[0]=p->ptr[s]; //后一半移入结点q
for(i=s+1;i<=m;i++){
q->key[i-s]=p->key[i];
q->ptr[i-s]=p->ptr[i];
}
q->keynum=p->keynum-s;
q->parent=p->parent;
for(i=0;i<=p->keynum-s;i++) //修改双亲指针
if(q->ptr[i]!=NULL)
q->ptr[i]->parent=q;
p->keynum=s-1; //结点p的前一半保留,修改结点p的keynum
}
void NewRoot(BTNode *&t,KeyType k,BTNode *p,BTNode *q){
//生成新的根结点t,原p和q为子树指针
t=(BTNode *)malloc(sizeof(BTNode)); //分配空间
t->keynum=1;
t->ptr[0]=p;
t->ptr[1]=q;
t->key[1]=k;
if(p!=NULL) //调整结点p和结点q的双亲指针
p->parent=t;
if(q!=NULL)
q->parent=t;
t->parent=NULL;
}
void InsertBTree(BTree &t,int i,KeyType k,BTNode *p){
/*在树t上结点q的key[i]与key[i+1]之间插入关键字k。若引起
结点过大,则沿双亲链进行必要的结点分裂调整,使t仍是B树*/
BTNode *q;
int finish_tag,newroot_tag,s; //设定需要新结点标志和插入完成标志
KeyType x;
if(p==NULL) //t是空树
NewRoot(t,k,NULL,NULL); //生成仅含关键字k的根结点t
else{
x=k;
q=NULL;
finish_tag=0;
newroot_tag=0;
while(finish_tag==0&&newroot_tag==0){
InsertBTNode(p,i,x,q); //将关键字x和结点q分别插入到p->key[i+1]和p->ptr[i+1]
if (p->keynum<=Max)
finish_tag=1; //插入完成
else{
s=(m+1)/2;
SplitBTNode(p,q); //分裂结点
x=p->key[s];
if(p->parent){ //查找x的插入位置
p=p->parent;
i=SearchBTNode(p, x);
}
else //没找到x,需要新结点
newroot_tag=1;
}
}
if(newroot_tag==1) //根结点已分裂为结点p和q
NewRoot(t,x,p,q); //生成新根结点t,p和q为子树指针
}
}
void Remove(BTNode *p,int i){
//从p结点删除key[i]和它的孩子指针ptr[i]
int j;
for(j=i+1;j<=p->keynum;j++){ //前移删除key[i]和ptr[i]
p->key[j-1]=p->key[j];
p->ptr[j-1]=p->ptr[j];
}
p->keynum--;
}
void Substitution(BTNode *p,int i){
//查找被删关键字p->key[i](在非叶子结点中)的替代叶子结点(右子树中值最小的关键字)
BTNode *q;
for(q=p->ptr[i];q->ptr[0]!=NULL;q=q->ptr[0]);
p->key[i]=q->key[1]; //复制关键字值
}
void MoveRight(BTNode *p,int i){
/*将双亲结点p中的最后一个关键字移入右结点q中
将左结点aq中的最后一个关键字移入双亲结点p中*/
int j;
BTNode *q=p->ptr[i];
BTNode *aq=p->ptr[i-1];
for(j=q->keynum;j>0;j--){ //将右兄弟q中所有关键字向后移动一位
q->key[j+1]=q->key[j];
q->ptr[j+1]=q->ptr[j];
}
q->ptr[1]=q->ptr[0]; //从双亲结点p移动关键字到右兄弟q中
q->key[1]=p->key[i];
q->keynum++;
p->key[i]=aq->key[aq->keynum]; //将左兄弟aq中最后一个关键字移动到双亲结点p中
p->ptr[i]->ptr[0]=aq->ptr[aq->keynum];
aq->keynum--;
}
void MoveLeft(BTNode *p,int i){
/*将双亲结点p中的第一个关键字移入左结点aq中,
将右结点q中的第一个关键字移入双亲结点p中*/
int j;
BTNode *aq=p->ptr[i-1];
BTNode *q=p->ptr[i];
aq->keynum++; //把双亲结点p中的关键字移动到左兄弟aq中
aq->key[aq->keynum]=p->key[i];
aq->ptr[aq->keynum]=p->ptr[i]->ptr[0];
p->key[i]=q->key[1]; //把右兄弟q中的关键字移动到双亲节点p中
q->ptr[0]=q->ptr[1];
q->keynum--;
for(j=1;j<=aq->keynum;j++){ //将右兄弟q中所有关键字向前移动一位
aq->key[j]=aq->key[j+1];
aq->ptr[j]=aq->ptr[j+1];
}
}
void Combine(BTNode *p,int i){
/*将双亲结点p、右结点q合并入左结点aq,
并调整双亲结点p中的剩余关键字的位置*/
int j;
BTNode *q=p->ptr[i];
BTNode *aq=p->ptr[i-1];
aq->keynum++; //将双亲结点的关键字p->key[i]插入到左结点aq
aq->key[aq->keynum]=p->key[i];
aq->ptr[aq->keynum]=q->ptr[0];
for(j=1;j<=q->keynum;j++){ //将右结点q中的所有关键字插入到左结点aq
aq->keynum++;
aq->key[aq->keynum]=q->key[j];
aq->ptr[aq->keynum]=q->ptr[j];
}
for(j=i;j<p->keynum;j++){ //将双亲结点p中的p->key[i]后的所有关键字向前移动一位
p->key[j]=p->key[j+1];
p->ptr[j]=p->ptr[j+1];
}
p->keynum--; //修改双亲结点p的keynum值
free(q); //释放空右结点q的空间
}
void AdjustBTree(BTNode *p,int i){
//删除结点p中的第i个关键字后,调整B树
if(i==0) //删除的是最左边关键字
if(p->ptr[1]->keynum>Min) //右结点可以借
MoveLeft(p,1);
else //右兄弟不够借
Combine(p,1);
else if(i==p->keynum) //删除的是最右边关键字
if(p->ptr[i-1]->keynum>Min) //左结点可以借
MoveRight(p,i);
else //左结点不够借
Combine(p,i);
else if(p->ptr[i-1]->keynum>Min) //删除关键字在中部且左结点够借
MoveRight(p,i);
else if(p->ptr[i+1]->keynum>Min) //删除关键字在中部且右结点够借
MoveLeft(p,i+1);
else //删除关键字在中部且左右结点都不够借
Combine(p,i);
}
int FindBTNode(BTNode *p,KeyType k,int &i){
//反映是否在结点p中是否查找到关键字k
if(k<p->key[1]){ //结点p中查找关键字k失败
i=0;
return 0;
}
else{ //在p结点中查找
i=p->keynum;
while(k<p->key[i]&&i>1)
i--;
if(k==p->key[i]) //结点p中查找关键字k成功
return 1;
}
}
int BTNodeDelete(BTNode *p,KeyType k){
//在结点p中查找并删除关键字k
int i;
int found_tag; //查找标志
if(p==NULL)
return 0;
else{
found_tag=FindBTNode(p,k,i); //返回查找结果
if(found_tag==1){ //查找成功
if(p->ptr[i-1]!=NULL){ //删除的是非叶子结点
Substitution(p,i); //寻找相邻关键字(右子树中最小的关键字)
BTNodeDelete(p->ptr[i],p->key[i]); //执行删除操作
}
else
Remove(p,i); //从结点p中位置i处删除关键字
}
else
found_tag=BTNodeDelete(p->ptr[i],k); //沿孩子结点递归查找并删除关键字k
if(p->ptr[i]!=NULL)
if(p->ptr[i]->keynum<Min) //删除后关键字个数小于MIN
AdjustBTree(p,i); //调整B树
return found_tag;
}
}
void BTreeDelete(BTree &t,KeyType k){
//构建删除框架,执行删除操作
BTNode *p;
int a=BTNodeDelete(t,k); //删除关键字k
if(a==0) //查找失败
printf(" 关键字%d不在B树中\n",k);
else if(t->keynum==0){ //调整
p=t;
t=t->ptr[0];
free(p);
}
}
void DestroyBTree(BTree &t){
//递归释放B树
int i;
BTNode* p=t;
if(p!=NULL){ //B树不为空
for(i=0;i<=p->keynum;i++){ //递归释放每一个结点
DestroyBTree(*&p->ptr[i]);
}
free(p);
}
t=NULL;
}
Status InitQueue(LinkList &L){
//初始化队列
L=(LNode*)malloc(sizeof(LNode)); //分配结点空间
if(L==NULL) //分配失败
return OVERFLOW;
L->next=NULL;
return OK;
}
LNode* CreateNode(BTNode *p){
//新建一个结点
LNode *q;
q=(LNode*)malloc(sizeof(LNode)); //分配结点空间
if(q!=NULL){ //分配成功
q->data=p;
q->next=NULL;
}
return q;
}
Status Enqueue(LNode *p,BTNode *q){
//元素q入队列
if(p==NULL)
return ERROR;
while(p->next!=NULL) //调至队列最后
p=p->next;
p->next=CreateNode(q); //生成结点让q进入队列
return OK;
}
Status Dequeue(LNode *p,BTNode *&q){
//出队列,并以q返回值
LNode *aq;
if(p==NULL||p->next==NULL) //删除位置不合理
return ERROR;
aq=p->next; //修改被删结点aq的指针域
p->next=aq->next;
q=aq->data;
free(aq); //释放结点aq
return OK;
}
Status IfEmpty(LinkList L){
//队列判空
if(L==NULL) //队列不存在
return ERROR;
if(L->next==NULL) //队列为空
return TRUE;
return FALSE; //队列非空
}
void DestroyQueue(LinkList L){
//销毁队列
LinkList p;
if(L!=NULL){
p=L;
L=L->next;
free(p); //逐一释放
DestroyQueue(L);
}
}
Status Traverse(BTree t,LinkList L,int newline,int sum){
//用队列遍历输出B树
int i;
BTree p;
if(t!=NULL){
printf(" [ ");
Enqueue(L,t->ptr[0]); //入队
for(i=1;i<=t->keynum;i++){
printf(" %d ",t->key[i]);
Enqueue(L,t->ptr[i]); //子结点入队
}
sum+=t->keynum+1;
printf("]");
if(newline==0){ //需要另起一行
printf("\n");
newline=sum-1;
sum=0;
}
else
newline--;
}
if(IfEmpty(L)==FALSE){ //l不为空
Dequeue(L,p); //出队,以p返回
Traverse(p,L,newline,sum); //遍历出队结点
}
return OK;
}
Status PrintBTree(BTree t){
//输出B树
LinkList L;
if(t==NULL){
printf(" B树为空树");
return OK;
}
InitQueue(L); //初始化队列
Traverse(t,L,0,0); //利用队列输出
DestroyQueue(L); //销毁队列
return OK;
}
void Test1(){
system("color 70");
BTNode *t=NULL;
Result s; //设定查找结果
int j,n=15;
KeyType k;
KeyType a[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
printf("创建一棵%d阶B树:\n",m);
for(j=0;j<n;j++){ //逐一插入元素
s=SearchBTree(t,a[j]);
if(s.tag==0)
InsertBTree(t,s.i,a[j],s.pt);
printf(" 第%d步,插入元素%d:\n ",j+1,a[j]);
PrintBTree(t);
}
printf("\n");
printf("删除操作:\n"); //删除操作
k=9;
BTreeDelete(t,k);
printf(" 删除%d:\n ",k);
printf(" 删除后的B树: \n");
PrintBTree(t);
printf("\n");
k=1;
BTreeDelete(t,k);
printf(" 删除%d:\n ",k);
printf(" 删除后的B树: \n");
PrintBTree(t);
printf("\n");
printf(" 递归释放B树\n"); //递归释放B树
DestroyBTree(t);
PrintBTree(t);
}
void Test2(){
int i,k;
system("color 70");
BTree t=NULL;
Result s; //设定查找结果
while(1){
printf("此时的B树:\n");
PrintBTree(t);
printf("\n");
printf("=============Operation Table=============\n");
printf(" 1.Init 2.Insert 3.Delete \n");
printf(" 4.Destroy 5.Exit \n");
printf("=========================================\n");
printf("Enter number to choose operation:_____\b\b\b");
scanf("%d",&i);
switch(i){
case 1:{
InitBTree(t);
printf("InitBTree successfully.\n");
break;
}
case 2:{
printf("Enter number to InsertBTree:_____\b\b\b");
scanf("%d",&k);
s=SearchBTree(t,k);
InsertBTree(t,s.i,k,s.pt);
printf("InsertBTree successfully.\n");
break;
}
case 3:{
printf("Enter number to DeleteBTree:_____\b\b\b");
scanf("%d",&k);
BTreeDelete(t,k);
printf("\n");
printf("DeleteBTree successfully.\n");
break;
}
case 4:{
DestroyBTree(t);
break;
printf("DestroyBTree successfully.\n");
}
case 5:{
exit(-1);
break;
}
}
}
}
int main(){
Test2();
return 0;
}

卡了下代码量
521lines
Merry christmas!

Thanks for your reward!